library(tidyverse)
library(ggplot2)
library(ggalluvial)
library(biomaRt)
library(ggdendro)
library(pcaMethods)
library(uwot)
library(pheatmap)
library(ggplotify)
library (ggrepel)
library(ggraph)
library(patchwork)



select <- dplyr::select
tmm_sample <- read_csv("./data/final_data/curated_pTMM_rattus_norvegicus_v103.csv")
Rows: 22245 Columns: 353── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (1): target_id
dbl (352): ventral.forebrain_male.3, ventral.forebrain_male.2, ventral.forebrain_female.3, ventral.forebrain_female...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
metadata <- read_csv("./data/final_data/curated_metadata.csv")
Rows: 352 Columns: 13── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (11): ID, Acronym, sex, tissue_name, region_tissue_name, consensus_tissue_name, organ_name, tissue_color, regio...
dbl  (1): rat_n
lgl  (1): regional_tissue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
all(sort(colnames(tmm_sample[-1])) == sort(metadata$ID))
[1] TRUE

#Generate hierarchical data



#slow, but understanable
tmm_tissue <- tmm_sample %>% 
  gather(sample, tmm, -1) %>% 
  left_join(metadata %>% select(ID,tissue_name), by = c("sample" = "ID")) %>% 
  group_by(target_id, tissue_name) %>% 
  summarize(tmm = mean(tmm)) %>% 
  ungroup()
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
write_csv(tmm_tissue, file="./data/final_data/final_tmm_tissue_name.csv")

tmm_region_tissue <- tmm_tissue %>% 
  left_join(metadata %>% select(tissue_name,region_tissue_name), by = c("tissue_name" = "tissue_name")) %>% 
  group_by(target_id, region_tissue_name) %>% 
  summarize(tmm = max(tmm)) %>% 
  ungroup()
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
write_csv(tmm_region_tissue, file="./data/final_data/final_tmm_region_tissue_name.csv")

tmm_consensus_tissue <- tmm_region_tissue %>% 
  left_join(metadata %>% select(region_tissue_name,consensus_tissue_name), by = c("region_tissue_name" = "region_tissue_name")) %>% 
  group_by(target_id, consensus_tissue_name) %>% 
  summarize(tmm = max(tmm)) %>% 
  ungroup()
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
write_csv(tmm_consensus_tissue, file="./data/final_data/final_tmm_consensus_name.csv")

#check
all(metadata$tissue_name %>% unique() %>% sort() == tmm_tissue$tissue_name %>% unique() %>% sort())
[1] TRUE
all(metadata$region_tissue_name %>% unique() %>% sort() == tmm_region_tissue$region_tissue_name %>% unique() %>% sort())
[1] TRUE
all(metadata$consensus_tissue_name %>% unique() %>% sort() == tmm_consensus_tissue$consensus_tissue_name %>% unique() %>% sort())
[1] TRUE
# # faster, but less understandable
# unique_tissue <- select(metadata,tissue_name) %>%
#   distinct()
# tmm_by_tissue <- tibble(target_id = tmm_sample$target_id)
# for (i in unique_tissue$tissue_name) {
#   curent_tissue_meta = metadata[metadata$tissue_name == i,]
#   current_tmm = select(tmm_sample,c(target_id, curent_tissue_meta$ID))
#   means <- current_tmm %>% select(curent_tissue_meta$ID) %>% rowMeans()
#   tmm_by_tissue[toString(i)] <- means
# }
# #tmm_by_tissue <- tmm_by_tissue %>% gather(tissue_name, tmm, -1)
# #write.csv(tmm_by_tissue, file="tmm_tissue_name.csv", row.names = FALSE)
# 
# unique_region <- select(metadata,region_tissue_name) %>%
#   distinct()
# tmm_by_region <- tibble(target_id = tmm_sample$target_id)
# for (i in unique_region$region_tissue_name) {
#   current_region_meta = metadata[metadata$region_tissue_name == i,]
#   current_tmm = select(tmm_by_tissue, c(target_id, unique(current_region_meta$tissue_name)))
#   max <- select(current_tmm,-target_id) %>% apply(1,max)
#   tmm_by_region[toString(i)] <- max
# }
# 
# # write.csv(tmm_by_region, file="tmm_region_name.csv", row.names = FALSE)
# 
# unique_consensus <- select(metadata,consensus_tissue_name) %>%
#   distinct()
# tmm_by_consensus <- tibble(target_id = tmm_sample$target_id)
# for (i in unique_consensus$consensus_tissue_name) {
#   current_consensus_meta = metadata[metadata$consensus_tissue_name == i,]
#   current_tmm = select(tmm_by_region, c(target_id, unique(current_consensus_meta$region_tissue_name)))
#   max <- select(current_tmm,-target_id) %>% apply(1,max)
#   tm_by_consensus[toString(i)] <- max
# }

#write.csv(tmm_by_consensus, file="tmm_consensus_name.csv", row.names = FALSE)

#Figure 1 ##Figure 1A - Hierarchy Overview

tissue_colors_palette_full <- rbind(
  metadata %>% select(name = tissue_name, color = tissue_color), 
  metadata %>% select(name = consensus_tissue_name, color = consensus_tissue_color),
  metadata %>% select(name = organ_name, color = organ_color)
) %>% distinct() %>% 
  mutate(name = str_to_sentence(name)) %>% arrange(name)

pal <- tissue_colors_palette_full$color
pal <- set_names(pal,tissue_colors_palette_full$name )


plot_data1 <- 
  metadata %>%
  select(tissue_name, region_tissue_name, consensus_tissue_name, organ_name) %>% #,
         #tissue_color, region_tissue_color, consensus_tissue_color, organ_color) %>% 
  mutate(tissue_name = str_to_sentence(tissue_name), region_tissue_name = str_to_sentence(region_tissue_name), consensus_tissue_name = str_to_sentence(consensus_tissue_name), organ_name = str_to_sentence( organ_name)) %>%   unique() %>% 
  mutate(organ_name = factor(case_when(organ_name == "Male reproductive system" ~ 
                                         "Male tissues",
                                       organ_name == "Breast and female reproductive system" ~
                                         "Female tissues",
                                       organ_name == "Adipose & soft tissue" ~ 
                                         "Connective & soft tissue",
                                       organ_name == "Bone marrow & immune system" ~
                                         "Bone marrow & lymphoid tissues",
                                       T ~ organ_name),
                             c("Brain",
                               "Eye",
                               "Endocrine tissues",
                               "Respiratory system",
                               "Proximal digestive tract",
                               "Gastrointestinal tract",
                               "Liver & gallbladder",
                               "Kidney & urinary bladder",
                               "Pancreas",
                               "Male tissues",
                               "Female tissues",
                               "Muscle tissues",
                               "Connective & soft tissue",
                               "Skin",
                               "Bone marrow & lymphoid tissues")))

plot_data1 <- plot_data1 %>% 
  arrange(organ_name,
          consensus_tissue_name,
          region_tissue_name,
          tissue_name) %>% 
  mutate(plot_order = row_number())

plot_data2 <- 
  plot_data1 %>%
  select(-region_tissue_name) %>%
  gather(column, label, -plot_order) %>%
  group_by(label, column) %>% 
  summarise(plot_order = mean(plot_order))  %>%
  ungroup() %>% 
  mutate(label = label,
         column = factor(column,
                         c("organ_name", 
                           "consensus_tissue_name", 
                           "tissue_name")))
Warning: attributes are not identical across measure variables;
they will be dropped`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
plot_data3 <- 
  plot_data1 %>% 
  group_by(consensus_tissue_name) %>% 
  mutate(left_pos = mean(plot_order))

plot_data4 <- 
  plot_data2 %>% 
  left_join(tissue_colors_palette_full,
            by = c("label" = "name")) %>% 
  group_by(column, label) %>% 
  summarise(miny = min(plot_order) - 0.5,
            maxy = max(plot_order) + 0.5)
`summarise()` has grouped output by 'column'. You can override using the `.groups` argument.
ggplot() +
  geom_rect(data = plot_data4, 
           aes(xmin = column, xmax = column, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F
         ) +
    geom_rect(data = plot_data4 %>% filter (column == "tissue_name"), 
          # aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color), 
           aes(xmin = 3, xmax = 4, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F
         ) +
    geom_rect(data = plot_data4 %>% filter (column == "consensus_tissue_name"), 
          # aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color), 
           aes(xmin = 1, xmax = 2, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F, width = 10
         ) +
  geom_segment(
    data = plot_data3,
    aes(
      x = "consensus_tissue_name",
      xend = "tissue_name",
      y = left_pos,
      yend = plot_order,
      color = tissue_name
    ),
    show.legend = F,
    alpha = 0.5,
    size = 2
  )+
  geom_text(
    data = plot_data2 %>% filter(column == "consensus_tissue_name"),
    aes(x = column, y = plot_order, label = label),
    hjust = 1,
    size = 2 * 5 / 6,
    label.padding = unit(0, "mm")
  ) +
  geom_text(
    data = plot_data2 %>% filter(column == "tissue_name"),
    aes(x = column, y = plot_order, label = label),
    hjust = 0,
    size = 2 * 5 / 6,
    show.legend = F,
    label.padding = unit(0, "mm")
  ) +
  geom_label(
    data = plot_data2 %>%
      filter(column == "organ_name"),
    #aes(x = column, y = plot_order, label = label),
    aes(x = column, y = plot_order, label = gsub(" ", "\n", label)),
    show.legend = F,
    label.size = 0,
    hjust = 1,
    lineheight = 0.7,
    label.padding = unit(0, "mm"),
    size = 2 * 5 / 6
  ) +
  scale_y_reverse() +
  scale_x_discrete(labels  = c('Organ system', "Grouped tissue", "Tissue type"),position = "top") +
  scale_fill_manual(values = pal) +
  scale_color_manual(values = pal) +
  theme_void() +
  theme(axis.text.x = element_text())
Warning: Ignoring unknown parameters: `width`Warning: Ignoring unknown parameters: `label.padding`Warning: Ignoring unknown parameters: `label.padding`
ggsave("final_plots/tissues.pdf", height = 7, width = 4)

##Figure 1B - Ward’s Retina-Dendrogramm


##Functino based on Max Karlsson's retinogramm
circular_dendrogram_retinastyle_2 <-
  function(clust, color_mapping, label_col, color_col, 
           scale_expansion = c(0.25, 0.25), text_size = 3, width_range = c(1.5, 6), 
           arc_strength = 0.8, default_color = "gray80") {
    require(ggraph)
    require(igraph)
    require(viridis)
    require(tidyverse)
    require(magrittr)
    
    dendrogram <-
      clust %>%
      as.dendrogram()
    
    
    
    g <-
      ggraph(dendrogram, layout = 'dendrogram', circular = T)
    # 
    # g +
    #   geom_edge_fan(data = edge_data %>%
    #                    mutate(hghl = edge.id == 99),
    #                  aes(label = edge_id, color = as.factor(rank_radius)),
    #                  width =4) +
    #   geom_node_text(aes(label = label))
    
    edge_data <- 
      get_edges()(g$data) %>%
      as_tibble() %>%
      left_join(color_mapping %>%
                  select(label = label_col,
                         color = color_col),
                by = c("node2.label" = "label")) %>%
      mutate(radius = xend^2 + yend^2,
             edge.id = as.character(edge.id)) %>%
      arrange(-radius) %>%
      mutate(edge_id = as.character(row_number()),
             rank_radius = unclass(factor(-radius)),
             x_m = round(x, 10),
             y_m = round(y, 10),
             xend_m = round(xend, 10),
             yend_m = round(yend, 10)) 
    
    
    edge_id_colors <- 
      edge_data %>% 
      filter(!is.na(color)) %$%
      set_names(color, edge_id)
    
    
    for(rank_rad in 2:max(edge_data$rank_radius)) {
      edge_id_colors_new <- 
        left_join(edge_data %>%
                    select(edge_id, radius, xend_m, yend_m, rank_radius) %>%
                    filter(rank_radius == rank_rad),
                  edge_data %>%
                    select(edge_id, radius, x_m, y_m, rank_radius) %>%
                    filter(rank_radius < rank_rad),
                  by = c("xend_m" = "x_m", "yend_m" = "y_m")) %>%
        left_join(enframe(edge_id_colors),
                  by = c("edge_id.y" = "name")) %>%
        group_by(edge_id.x) %>% 
        summarise(color = ifelse(n_distinct(value) == 1 & any(value != default_color), 
                                 as.character(unique(value)),
                                 default_color)) %$%
        set_names(color, edge_id.x)
      edge_id_colors <- 
        c(edge_id_colors, edge_id_colors_new)
    }
    
    
    
    g +
      scale_edge_width(range = width_range)+
      geom_edge_diagonal(data = edge_data,
                         aes(edge_color = as.character(edge_id),
                             edge_width = 1 - sqrt(xend^2 + yend^2)),
                         strength = arc_strength,
                         show.legend = F) +
      
      
      scale_edge_color_manual(values = edge_id_colors)  +
      g$data %>%
      filter(label != "") %>%
      mutate(degree = case_when(x >= 0 ~ asin(y) * 180 / pi,
                                x < 0 ~ 360 - asin(y) * 180 / pi)) %>%
      left_join(color_mapping %>%
                  select(label = label_col,
                         color = color_col),
                by = "label") %>%
                {geom_node_text(data = .,
                                aes(label = label),
                                angle = .$degree,
                                hjust = ifelse(.$x < 0, 
                                               1, 
                                               0),
                                vjust = 0.5,
                                size = text_size)}  +
      scale_x_continuous(expand = expand_scale(scale_expansion)) +
      scale_y_continuous(expand = expand_scale(scale_expansion)) +
      
      coord_fixed() +
      theme_void()
  }

hclust4RNAseq_ward <- function(df, correlation_method = "spearman"){
  #wide dataframe as input 
  #to get correlation between samples, where rows are genes columns are samples
  #to get correlation between genes across samples, input df with genes as columns
  #can use later for dendogram making: ggdendrogram([hclust4RNAseq_results], rotate = FALSE, size = 10, face = "bold")
  similarity <- cor(df, method=correlation_method, use="pairwise.complete.obs")
  dissimilarity <- 1 - similarity
  hcl <- hclust(as.dist(dissimilarity), "ward.D2")
  return (hcl)
} 

tissue_dendro_ward <- hclust4RNAseq_ward(tmm_tissue %>% mutate(tissue_name = str_to_sentence(tissue_name)) %>%  spread(tissue_name, tmm) %>% column_to_rownames("target_id"))

circular_dendrogram_retinastyle_2(
  clust = tissue_dendro_ward, 
  color_mapping = metadata %>% 
    select(tissue_name, tissue_color) %>% 
    mutate(tissue_name = str_to_sentence(tissue_name)), 
  label_col = "tissue_name", 
  color_col = "tissue_color", 
  scale_expansion = c(0.7, 0.7), 
  text_size = 2.4, 
  width_range = c(0.5, 4),
  arc_strength = 0.4, 
  default_color = "gray80")

ggsave("./final_plots/data_presentation/retinagram_all_tissue_clust_ward.pdf", width = 6, height = 6)

##Figure 1C - Spearman heatmap (grouped tissue)

##Spearman's roh heatmap at grouped tissue level

if(file.exists("./data/final_data/spearman_corr_consensus_tissues.csv")) {
  consensus_tmm_spearman <- read_csv("./data/final_data/spearman_corr_consensus_tissues.csv")
} else {
  consensus_tmm_spearman <-  tmm_consensus_tissue %>% 
    spread(consensus_tissue_name, tmm) %>% 
    column_to_rownames("target_id") %>% 
    cor(method="spearman", use="pairwise.complete.obs") %>% 
    as.data.frame() %>% 
    as_tibble(rownames = "consensus_tissue_name")
  write_csv(consensus_tmm_spearman,"./data/final_data/spearman_corr_consensus_tissues.csv")
}
Rows: 53 Columns: 54── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (1): consensus_tissue_name
dbl (53): adipose tissue, adrenal gland, aorta, bone marrow, brain, breast, cartilage, cervix, choroid plexus, circ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
consensus_tmm_spearman %>% 
  rename_with(str_to_sentence, -1) %>% 
  mutate(consensus_tissue_name = str_to_sentence(consensus_tissue_name)) %>% 
  column_to_rownames("consensus_tissue_name") %>% 
  pheatmap(
   # clustering_method = "ward.D2",
    cellheight = 8,
    cellwidth = 8, 
    border_color = NA,
    color = viridis::inferno(20, direction = -1),
    show_rownames = FALSE, 
    ) %>% 
  as.ggplot()

ggsave("./final_plots/data_presentation/spearman_corr_consensus_tissue.pdf", height = 10, width = 10)

#Figure 2 ##Figure 2A - Sample level PCA Plot


sample_pca <-
  tmm_sample %>% 
  
  # gather(sample_name, tmm, -1) %>%
  # group_by(target_id) %>%
  # mutate(sd = sd(tmm)) %>%
  # ungroup() %>%
  # filter(sd > 0) %>%
  # select(-sd) %>%
  # spread(sample_name, tmm) %>%
  
  mutate_if(is.numeric, function(x){log10(x+1)}) %>%
  column_to_rownames("target_id") %>%
  scale() %>%
  t() %>%
  pca(nPcs = 8)

#sample_pca@scores

summary(sample_pca)
svd calculated PCA
Importance of component(s):
                 PC1     PC2     PC3     PC4     PC5     PC6     PC7     PC8
R2            0.3135 0.09126 0.08083 0.03926 0.03616 0.03489 0.02711 0.02105
Cumulative R2 0.3135 0.40474 0.48557 0.52483 0.56099 0.59588 0.62299 0.64404
plot_data <- sample_pca %>% 
  scores() %>% 
  as_tibble(rownames = "sample_id") %>% 
  left_join(metadata,
            by = c("sample_id" = "ID"))


organ_colors <- metadata %>% select(organ_name, organ_color) %>% unique()
pal <-  organ_colors$organ_color
pal <- setNames(pal, organ_colors$organ_name)

plot_data %>%
  ggplot(aes(PC1, PC2)) +
  geom_point(aes(fill = organ_name), color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
  scale_fill_manual(values = pal) + 
  #geom_text(aes(label = sample_id), vjust = 1,hjust = 0, nudge_y =-1) +
  xlab(paste("PC1", sample_pca@R2[1] * 100, "% of the variance")) +
  ylab(paste("PC2", sample_pca@R2[2] * 100, "% of the variance")) +
  theme_classic() + theme(legend.text = element_text(size = 10),
                          legend.title =element_blank(), 
                          legend.position = "bottom",
                          legend.spacing.y = unit(-0.3, 'cm'),
                          legend.spacing.x = unit(-0.01, 'cm')) +
  guides(fill = guide_legend(ncol = 3, byrow = TRUE)) 



#ggsave("./final_plots/data_presentation/sample_pca_1.pdf", width = 8, height = 8)
# ggsave("./final_plots/data_presentation/sample_pca_1_w_filter.pdf", width = 8, height = 8)
# 
# plot_data %>%
#   ggplot(aes(PC1, PC2)) +
#   geom_point(aes(fill = organ_name), color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
#   scale_fill_manual(values = pal) + 
#   #geom_text(aes(label = sample_id), vjust = 1,hjust = 0, nudge_y =-1) +
#   xlab(paste("PC1", sample_pca@R2[1] * 100, "% of the variance")) +
#   ylab(paste("PC2", sample_pca@R2[2] * 100, "% of the variance")) +
#   theme_classic() + theme(legend.text = element_text(size = 10),
#                           legend.title =element_blank(), 
#                           legend.position = "bottom",
#                           legend.spacing.y = unit(-0.3, 'cm'),
#                           legend.spacing.x = unit(-0.01, 'cm')) +
#   guides(fill = guide_legend(ncol = 2, byrow = TRUE)) 
# 
# #ggsave("./final_plots/data_presentation/sample_pca_2.pdf", width = 5, height = 5)
# # ggsave("./final_plots/data_presentation/sample_pca_2_w_filter.pdf", width = 7, height = 7)

###Extra PCA plots not in report

sample_pca <-
  tmm_sample %>% 
  # gather(sample_name, tmm, -1) %>% 
  # group_by(target_id) %>%
  # mutate(sd = sd(tmm)) %>%
  # ungroup() %>% 
  # filter(sd > 0) %>% 
  # select(-sd) %>% 
  # spread(sample_name, tmm) %>% 
  mutate_if(is.numeric, function(x){log10(x+1)}) %>%
  column_to_rownames("target_id") %>%
  scale() %>%
  t() %>%
  pca(nPcs = 8)

#sample_pca@scores

summary(sample_pca)
svd calculated PCA
Importance of component(s):
                 PC1     PC2     PC3     PC4     PC5     PC6     PC7     PC8
R2            0.3135 0.09126 0.08083 0.03926 0.03616 0.03489 0.02711 0.02105
Cumulative R2 0.3135 0.40474 0.48557 0.52483 0.56099 0.59588 0.62299 0.64404
plot_data <- sample_pca %>% 
  scores() %>% 
  as_tibble(rownames = "sample_id") %>% 
  left_join(metadata,
            by = c("sample_id" = "ID"))


region_colors <- metadata %>% select(region_tissue_name, region_tissue_color, organ_name) %>% unique() %>% filter(organ_name == "Brain") %>% select(-organ_name)
pal <-  region_colors$region_tissue_color
pal <- setNames(pal, region_colors$region_tissue_name)

plot_data %>%
  ggplot(aes(PC1, PC2)) +
  geom_point(aes(fill = region_tissue_name),  color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
  scale_fill_manual(values = pal, na.value = "#FFFFFF") + 
  xlim(-75,0) +
  ylim(-10,5) +
  xlab(paste("PC1", sample_pca@R2[1] * 100, "% of the variance")) +
  ylab(paste("PC2", sample_pca@R2[2] * 100, "% of the variance")) +
  theme_classic() + theme(legend.text = element_text(size = 10),
                          legend.title =element_blank(), 
                          legend.position = "bottom",
                          legend.spacing.y = unit(-0.3, 'cm'),
                          legend.spacing.x = unit(-0.01, 'cm')) +
  guides(fill = guide_legend(ncol = 2, byrow = TRUE)) 

ggsave("./final_plots/data_presentation/brain_w_all_sample_pca.pdf", width = 5, height = 5)

NA
NA

brain_pca <-
  tmm_sample %>% select(c(target_id, metadata %>% filter(organ_name =="Brain") %>% .$ID))%>% 
  mutate_if(is.numeric, function(x){log10(x+1)}) %>%
  column_to_rownames("target_id") %>%
  scale() %>%
  t() %>%
  pca(nPcs = 8)

#brain_pca@scores


plot_data <- brain_pca %>% 
  scores() %>% 
  as_tibble(rownames = "sample_id") %>% 
  left_join(metadata,
            by = c("sample_id" = "ID"))


region_colors <- metadata %>% select(region_tissue_name, region_tissue_color) %>% unique()
pal <-  region_colors$region_tissue_color
pal <- setNames(pal, region_colors$region_tissue_name)

plot_data %>%
  ggplot(aes(PC1, PC2)) +
  geom_point(aes(fill = region_tissue_name), color = "gray25", alpha = 0.7, shape=21, size = 2, stroke = 0.5) +
  scale_fill_manual(values = pal) + 
  #geom_text(aes(label = sample_id), vjust = 1,hjust = 0, nudge_y =-1) +
  xlab(paste("PC1", brain_pca@R2[1] * 100, "% of the variance")) +
  ylab(paste("PC2", brain_pca@R2[2] * 100, "% of the variance")) +
  theme_classic() + theme(legend.text = element_text(size = 10),
                          legend.title =element_blank(), 
                          legend.position = "bottom",
                          legend.spacing.y = unit(-0.3, 'cm'),
                          legend.spacing.x = unit(-0.01, 'cm')) +
  guides(fill = guide_legend(ncol = 2, byrow = TRUE)) 

ggsave("./final_plots/data_presentation/brain_sample_pca.pdf", width = 5, height = 5)

NA
NA
NA

##Figure 2B - UMAP Plot

wide_data = tmm_sample 
seed = 4
n_epochs = 1000
n_neighbors = 15
set.seed(seed)
    
pca_res <-
  wide_data %>% 
  mutate_if(is.numeric, function(x){log10(x+1)}) %>% 
  column_to_rownames(colnames(wide_data)[1]) %>%
  scale() %>%
  t() %>%
  pca(nPcs = dim(.)[1])
    
    
pc_lim <-
  which(pca_res@R2cum > 0.8)[1]

pc_lim_sd <-
  rev(which(pca_res@sDev > 1))[1]

set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
  umap(n_neighbors = n_neighbors,
       n_epochs = n_epochs) %>%
       #metric = "correlation") %>%
  as_tibble() %>%
  set_names(paste0("UMAP", 1:ncol(.))) %>%
  mutate(sample = rownames(pca_res@scores)) %>%
  select(sample, everything()) %>% 
  left_join(metadata, by = c("sample" = "ID"))

organ_colors <- metadata %>% select(organ_name, organ_color) %>% unique()
pal <-  organ_colors$organ_color
pal <- setNames(pal, organ_colors$organ_name)

# umap_res %>%  ggplot(aes(UMAP1, UMAP2,  color = organ_name)) +
#    geom_point(alpha = 0.8) + scale_color_manual(values = pal)
umap_res %>%  ggplot(aes(UMAP1, UMAP2)) +
  geom_point(
    aes(fill = organ_name),
    color = "gray25",
    alpha = 0.7,
    shape = 21,
    size = 2,
    stroke = 0.5
  ) + scale_fill_manual(values = pal) + 
  # theme_bw() + theme(
  #   panel.border = element_blank(),
  #   panel.grid.major = element_blank(),
  #   panel.grid.minor = element_blank(),
  #   axis.line = element_line(colour = "black"),
  #   legend.title = element_blank()
  # )
  theme_classic() + theme(legend.text = element_text(size = 10),
                          legend.title =element_blank(), 
                          legend.position = "bottom",
                          legend.spacing.y = unit(-0.3, 'cm'),
                          legend.spacing.x = unit(-0.01, 'cm')) +
  guides(fill = guide_legend(ncol = 2, byrow = TRUE)) 

ggsave("./final_plots/data_presentation/sample_umap_euclidean.pdf", width = 5, height = 5.5)

##Figure 2C - Brain Umap Plot

#Plot focusing only on brain samples, but UMAP was based on the whole sample set, not only brian samples.

wide_data = tmm_sample 
seed = 4
n_epochs = 1000
n_neighbors = 15
set.seed(seed)
    
pca_res <-
  wide_data %>% 
  mutate_if(is.numeric, function(x){log10(x+1)}) %>% 
  column_to_rownames(colnames(wide_data)[1]) %>%
  scale() %>%
  t() %>%
  pca(nPcs = dim(.)[1])
    
    
pc_lim <-
  which(pca_res@R2cum > 0.8)[1]

pc_lim_sd <-
  rev(which(pca_res@sDev > 1))[1]

set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
  umap(n_neighbors = n_neighbors,
       n_epochs = n_epochs) %>%
       #metric = "correlation") %>%
  as_tibble() %>%
  set_names(paste0("UMAP", 1:ncol(.))) %>%
  mutate(sample = rownames(pca_res@scores)) %>%
  select(sample, everything()) %>% 
  left_join(metadata, by = c("sample" = "ID"))


region_colors <- metadata %>% select(region_tissue_name, region_tissue_color, organ_name) %>% unique() %>% filter(organ_name == "Brain") %>% select(-organ_name)
pal <-  region_colors$region_tissue_color
pal <- setNames(pal, region_colors$region_tissue_name)
# umap_res %>%  ggplot(aes(UMAP1, UMAP2,  color = organ_name)) +
#    geom_point(alpha = 0.8) + scale_color_manual(values = pal)
umap_res %>%  ggplot(aes(UMAP1, UMAP2)) +
  geom_point(
    aes(fill = region_tissue_name),
    color = "gray25",
    alpha = 0.7,
    shape = 21,
    size = 2,
    stroke = 0.5
  ) + 
  scale_fill_manual(values = pal, na.value = "#FFFFFF") + 
  # theme_bw() + theme(
  #   panel.border = element_blank(),
  #   panel.grid.major = element_blank(),
  #   panel.grid.minor = element_blank(),
  #   axis.line = element_line(colour = "black"),
  #   legend.title = element_blank()
  # )
  xlim(-11,-6) +
  ylim(-8,-2) +
  theme_classic() + theme(legend.text = element_text(size = 10),
                          legend.title =element_blank(), 
                          legend.position = "bottom",
                          legend.spacing.y = unit(-0.3, 'cm'),
                          legend.spacing.x = unit(-0.01, 'cm')) +
  guides(fill = guide_legend(ncol = 2, byrow = TRUE)) 

ggsave("./final_plots/data_presentation/sample_focus_brain_umap_euclidean.pdf", width = 5, height = 5.5)

###Extra UMAP plot not in report

#UMAP plot based only on brain samples, thus different than the plot above.

wide_data = tmm_sample %>% select(c(target_id, metadata %>% filter(organ_name == "Brain") %>% .$ID))
seed = 4
n_epochs = 1000
n_neighbors = 15
set.seed(seed)
    
pca_res <-
  wide_data %>% 
  mutate_if(is.numeric, function(x){log10(x+1)}) %>% 
  column_to_rownames(colnames(wide_data)[1]) %>%
  scale() %>%
  t() %>%
  pca(nPcs = dim(.)[1])
    
    
pc_lim <-
  which(pca_res@R2cum > 0.8)[1]

pc_lim_sd <-
  rev(which(pca_res@sDev > 1))[1]

set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
  umap(n_neighbors = n_neighbors,
       n_epochs = n_epochs) %>%
       #metric = "correlation") %>%
  as_tibble() %>%
  set_names(paste0("UMAP", 1:ncol(.))) %>%
  mutate(sample = rownames(pca_res@scores)) %>%
  select(sample, everything()) %>% 
  left_join(metadata, by = c("sample" = "ID"))

region_tissue_colors <- metadata %>% select(region_tissue_name, region_tissue_color) %>% unique()
pal <-  region_tissue_colors$region_tissue_color
pal <- setNames(pal, region_tissue_colors$region_tissue_name)

# umap_res %>%  ggplot(aes(UMAP1, UMAP2,  color = organ_name)) +
#    geom_point(alpha = 0.8) + scale_color_manual(values = pal)
umap_res %>%  ggplot(aes(UMAP1, UMAP2)) +
  geom_point(
    aes(fill = region_tissue_name),
    color = "gray25",
    alpha = 0.7,
    shape = 21,
    size = 2,
    stroke = 0.5
  ) + scale_fill_manual(values = pal) + 
  # theme_bw() + theme(
  #   panel.border = element_blank(),
  #   panel.grid.major = element_blank(),
  #   panel.grid.minor = element_blank(),
  #   axis.line = element_line(colour = "black"),
  #   legend.title = element_blank()
  # )
  theme_classic() + theme(legend.text = element_text(size = 10),
                          legend.title =element_blank(), 
                         # legend.position = "bottom",
                          legend.spacing.y = unit(-0.3, 'cm'),
                          legend.spacing.x = unit(-0.01, 'cm')) +
  guides(fill = guide_legend(ncol = 1, byrow = TRUE)) 

ggsave("./final_plots/data_presentation/sample_brain_umap_euclidean_l.pdf", width = 5.5, height = 3)

##Figure 2D - grouped sample to sample correlation plots

if(file.exists("./data/final_data/spearman_sample.csv")) {
  sample_tmm_spearman <- read_csv("./data/final_data/spearman_sample.csv")
} else {
  sample_tmm_spearman <-  tmm_sample %>%
    column_to_rownames("target_id") %>%
    cor(method = "spearman", use = "pairwise.complete.obs") %>%
    as.data.frame() %>%
    as_tibble(rownames = "sample_name")
  write_csv(as.data.frame(sample_tmm_spearman) %>% as_tibble(),"./data/final_data/spearman_sample.csv")
}
Rows: 352 Columns: 353── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (1): sample_name
dbl (352): ventral.forebrain_male.3, ventral.forebrain_male.2, ventral.forebrain_female.3, ventral.forebrain_female...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
correlation_to_different_organs <- tibble(sample_name = c(), correlation = c())
for (sample in metadata$ID){
  sample_organ <- metadata %>% filter(ID == sample) %>% .$organ_name %>% unique()
  different_organ_samples <- metadata %>% filter(organ_name != sample_organ) %>% .$ID
  sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% different_organ_samples) %>% select("sample_name", sample) %>% rename("correlation" = sample)
  correlation_to_different_organs <- rbind(correlation_to_different_organs, sample_correlation) 
}

correlation_to_same_organ <- tibble(sample_name = c(), correlation = c())
for (sample in metadata$ID){
  sample_organ <- metadata %>% filter(ID == sample) %>% .$organ_name %>% unique()
  same_organ_samples <- metadata %>% filter(organ_name == sample_organ) %>% filter(ID != sample) %>% .$ID
  sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% same_organ_samples) %>% select("sample_name", sample) %>% rename("correlation" = sample)
  correlation_to_same_organ <- rbind(correlation_to_same_organ, sample_correlation) 
}

correlation_to_same_tissue <- tibble(sample_name = c(), correlation = c())
for (sample in metadata$ID){
  sample_tissue <- metadata %>% filter(ID == sample) %>% .$tissue_name %>% unique()
  same_tissue_samples <- metadata %>% filter(tissue_name == sample_tissue) %>% filter(ID != sample) %>% .$ID
  sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% same_tissue_samples) %>% select("sample_name", sample) %>% rename("correlation" = sample)
  correlation_to_same_tissue <- rbind(correlation_to_same_tissue, sample_correlation) 
}

correlation_to_same_tissue_by_tissue <- tibble(sample_name = c(), correlation = c(), tissue_name = c())
for (sample in metadata$ID){
  sample_tissue <- metadata %>% filter(ID == sample) %>% .$tissue_name %>% unique()
  same_tissue_samples <- metadata %>% filter(tissue_name == sample_tissue) %>% .$ID
  sample_correlation <- sample_tmm_spearman %>% filter(sample_name %in% same_tissue_samples) %>% select("sample_name", sample) %>% filter(sample_name != sample) %>%  left_join(metadata %>% select(ID, tissue_name), by= c("sample_name" = "ID")) %>% rename("correlation" = sample)
  correlation_to_same_tissue_by_tissue <- rbind(correlation_to_same_tissue_by_tissue, sample_correlation) 
}

correlation_to_same_tissue_by_tissue <- correlation_to_same_tissue_by_tissue %>% 
  mutate(tissue_name = str_to_sentence(tissue_name)) %>% 
  group_by(tissue_name) %>% 
  mutate(min = min(correlation)) %>% 
  ungroup() %>% 
  arrange(min)

p1 <- correlation_to_different_organs %>%
  ggplot(aes(correlation)) +
  geom_histogram(bins = 100)  + xlim(0.5,1)+
  theme_classic() + 
  theme(panel.background = element_rect("gray90"),
        axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.y.left = element_blank(),
        axis.title.x = element_blank()) +
  scale_y_continuous(expand = expansion(mult = 0, add = 0)) + 
  ggtitle("Different organs")

p2 <- correlation_to_same_organ %>%
  ggplot(aes(correlation)) +
  geom_histogram(bins = 100) + xlim(0.5,1) +
  theme_classic() + 
  theme(panel.background = element_rect("gray90"),
        axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank()) +
  scale_y_continuous(expand = expansion(mult = 0, add = 0)) +
  ggtitle("Same organs")

p3 <- correlation_to_same_tissue %>%
  ggplot(aes(correlation)) +
  geom_histogram(bins = 100) + xlim(0.5,1) +
  theme_classic() +
  theme(panel.background = element_rect("gray90"),
        axis.title.y = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank()
        ) +
  scale_y_continuous(expand = expansion(mult = 0, add = 0)) +
  ggtitle("Same tissue")

p4 <- correlation_to_same_tissue_by_tissue %>% 
  mutate(tissue_name = factor(tissue_name, correlation_to_same_tissue_by_tissue$tissue_name %>% 
                                unique())) %>% 
  ggplot(aes(x = correlation, y = tissue_name)) +
  geom_boxplot(outlier.size=0.3) + xlim(0.5,1) +
  theme_bw() + 
  xlab ("Spearman's roh") +
  theme(axis.title.y = element_blank())

p1 / p2 / p3 / p4 + plot_layout(heights = c(1, 1, 1, 15))

ggsave("./final_plots/data_presentation/spearman_corr_sample_vs_tissue_organ.pdf", height = 14, width = 4)

#Specificity and distribution annotation ##Formulas

#calculate_tau_score and hpa_gene_classification taken from Max Karlsson
calculate_tau_score <- 
  function(wide_data) {
    max_exp <- 
      apply(wide_data,
            MARGIN = 1,
            function(x) max(x, na.rm = T))
    
    N <- 
      apply(wide_data,
            MARGIN = 1,
            function(x) length(which(!is.na(x))))
    
    expression_sum <- 
      wide_data %>% 
      sweep(MARGIN = 1, 
            STATS = max_exp, 
            FUN = `/`) %>% 
      {1 - .} %>% 
      apply(MARGIN = 1,
            function(x) sum(x, na.rm = T))
    
    
    tau_score <- 
      (expression_sum / (N - 1)) %>% 
      enframe("gene", "tau_score")
    
    tau_score
  }

hpa_gene_classification <- 
  #feed in long data
  function(data, expression_col, tissue_col, gene_col, enr_fold, max_group_n, det_lim = 1) {
    data_ <- 
      data %>% 
      select(gene = gene_col,
             expression = expression_col,
             tissue = tissue_col) %>% 
      mutate(expression = round(expression, 4)) 
    
    if(any(is.na(data_$expression))) stop("NAs in expression column")
    if(any(is.na(data_$gene))) stop("NAs in gene column")
    if(any(is.na(data_$tissue))) stop("NAs in tissue column")
    
    n_groups <- length(unique(data_$tissue))
    
    gene_class_info <- 
      data_ %>%
      group_by(gene) %>%
      summarise(
        
        # Gene expression distribution metrics
        mean_exp = mean(expression, na.rm = T),
        min_exp = min(expression, na.rm = T),
        max_exp = max(expression, na.rm = T), 
        max_2nd = sort(expression)[length(expression)-1],
        
        # Expression frequency metrics
        n_exp = length(which(expression >= det_lim)),
        frac_exp = n_exp/length(expression[!is.na(expression)])*100,
        
        # Limit of enhancement metrics
        lim = max_exp/enr_fold, 
        
        exps_over_lim = list(expression[which(expression >= lim & expression >= det_lim)]),
        n_over = length(exps_over_lim[[1]]), 
        mean_over = mean(exps_over_lim[[1]]),
        min_over = ifelse(n_over == 0, NA,
                          min(exps_over_lim[[1]])),
        
        max_under_lim = max(expression[which(expression < min_over)], det_lim*0.1),
        
        
        exps_enhanced = list(which(expression/mean_exp >= enr_fold & expression >= det_lim)),
        
        
        
        
        # Expression patterns
        enrichment_group = paste(sort(tissue[which(expression >= lim & expression >= det_lim)]), collapse=";"),
        
        n_enriched = length(tissue[which(expression >= lim & expression >= det_lim)]),
        n_enhanced = length(exps_enhanced[[1]]), 
        enhanced_in = paste(sort(tissue[exps_enhanced[[1]]]), collapse=";"),
        n_na = n_groups - length(expression),
        max_2nd_or_lim = max(max_2nd, det_lim*0.1),
        tissues_not_detected = paste(sort(tissue[which(expression < det_lim)]), collapse=";"),
        tissues_detected = paste(sort(tissue[which(expression >= det_lim)]), collapse=";")) 
    
    
    gene_categories <- 
      gene_class_info %>%
      
      mutate(
        spec_category = case_when(n_exp == 0 ~ "not detected", 
                                  
                                  # Genes with expression fold times more than anything else are tissue enriched
                                  max_exp/max_2nd_or_lim >= enr_fold ~ "tissue enriched", 
                                  
                                  # Genes with expression fold times more than other tissues in groups of max group_n - 1 are group enriched
                                  max_exp >= lim &
                                    n_over <= max_group_n & n_over > 1 &
                                    mean_over/max_under_lim >= enr_fold ~ "group enriched", 
                                  
                                  # Genes with expression in tissues fold times more than the mean are tissue enhance
                                  n_enhanced > 0 ~ "tissue enhanced", 
                                  
                                  # Genes expressed with low tissue specificity
                                  T ~ "low tissue specificity"), 
        
        
        dist_category = case_when(frac_exp == 100 ~ "detected in all",
                                  frac_exp >= 31 ~ "detected in many",
                                  n_exp > 1 ~ "detected in some",
                                  n_exp == 1 ~ "detected in single",
                                  n_exp == 0 ~ "not detected"),
        
        spec_score = case_when(spec_category == "tissue enriched" ~ max_exp/max_2nd_or_lim,
                               spec_category == "group enriched" ~ mean_over/max_under_lim, 
                               spec_category == "tissue enhanced" ~ max_exp/mean_exp)) 
    
    
    
    
    ##### Rename and format
    gene_categories %>%
      mutate(enriched_tissues = case_when(spec_category %in% c("tissue enriched", "group enriched") ~ enrichment_group,
                                          spec_category == "tissue enhanced" ~ enhanced_in),
             n_enriched = case_when(spec_category %in% c("tissue enriched", "group enriched") ~ n_enriched,
                                    spec_category == "tissue enhanced" ~ n_enhanced)) %>%
      select(gene, 
             spec_category, 
             dist_category, 
             spec_score,
             n_expressed = n_exp, 
             fraction_expressed = frac_exp,
             max_exp = max_exp,
             enriched_tissues,
             n_enriched,
             n_na = n_na,
             tissues_not_detected,
             tissues_detected) 
    
  } 

##Annotation


# Specificity classification at consensus level
classification_consensus <- hpa_gene_classification(data = tmm_consensus_tissue, expression_col = "tmm", tissue_col = "consensus_tissue_name", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1)

consensus_tau <- calculate_tau_score(tmm_consensus_tissue  %>% 
                                     spread(consensus_tissue_name, tmm)%>%  
                                       mutate_if(is.numeric, function(x){log10(x+1)})%>% 
                                       column_to_rownames("target_id")) 
#category not detected has a very noisy tau, so no tau score for those
classification_consensus <- classification_consensus %>% 
  left_join(consensus_tau, by = c("gene" = "gene")) %>% 
  mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score))

#at region level
classification_region <- hpa_gene_classification(data = tmm_region_tissue, expression_col = "tmm", tissue_col = "region_tissue_name", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1)

region_tau <- calculate_tau_score(tmm_region_tissue  %>% 
                                    spread(region_tissue_name, tmm) %>%  
                                    mutate_if(is.numeric, function(x){log10(x+1)}) %>% 
                                    column_to_rownames("target_id") )

classification_region <- classification_region %>% 
  left_join(region_tau, by = c("gene" = "gene")) %>% 
  mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score))

#at tissue level
classification_tissue <- hpa_gene_classification(data = tmm_tissue, expression_col = "tmm", tissue_col = "tissue_name", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1)

tissue_tau <- calculate_tau_score(tmm_tissue %>% 
                                    spread(tissue_name, tmm) %>%  
                                    mutate_if(is.numeric, function(x){log10(x+1)})%>%
                                    column_to_rownames("target_id") )

classification_tissue <- classification_tissue %>% 
  left_join(tissue_tau, by = c("gene" = "gene")) %>% 
  mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score))

#Figure 3 ##Figure 3A - Pie charts at tissue type level

gene_category_pal <- c("Detected in all" = "#253494",
                       "Detected in many" = "#2c7fb8",
                       "Detected in some" = "#41b6c4",
                       "Detected in single" = "#a1dab4",
                       "Not detected " = "  #bebebe",
                       "Low tissue specificity" = "grey40",
                       "Tissue enhanced" = "#984ea3",
                       "Group enriched" = "#FF9D00",
                       "Tissue enriched" = "#e41a1c")

plot_data <-
  classification_tissue %>% 
  rename(specificity = spec_category, distribution = dist_category) %>% 
  select(gene, specificity, distribution) %>% 
  gather(class_type, class, -1) %>% 
  group_by(class_type, class) %>% 
  count() %>%
  group_by(class_type) %>% 
  mutate(perc = 100 * n / sum(n),
         label = paste0(n, "\n", round(perc, 1), "%")) %>% 
  ungroup() %>% 
  mutate(class = factor(str_to_sentence(class), str_to_sentence(c("tissue enriched", "group enriched",  "tissue enhanced", "low tissue specificity", "detected in all", "detected in many", "detected in some", "detected in single", "not detected"))),
         class_type = factor(str_to_sentence(class_type),
                             c("Specificity", "Distribution")))
    
plot_dodge = 0.1
plot_data %>% 
  arrange(match(class, rev(levels(class)))) %>% 
  group_by(class_type) %>% 
  mutate(y_stack = cumsum(n) - n/2) %>% 
  {ggplot(., aes(1, n, fill = class, group = class, 
                 label = label)) +
      geom_col(show.legend = F, 
               color = "white", 
               width = 1) + 
      geom_segment(aes(x = 1.5 + plot_dodge, xend = 1.5, 
                       y = y_stack, yend = y_stack), size = 0.5) +
      geom_text_repel(aes(x = 1.5 + plot_dodge, y = y_stack), 
                      color = "black", nudge_x = plot_dodge, 
                      segment.size = 0.5, size = 24/11) +
      scale_fill_manual(values = gene_category_pal) + 
      facet_wrap(~class_type) +
      coord_polar("y",start = 0) +
      theme_void() + 
      scale_x_continuous(expand = expansion(c(0,0.8)))}


ggsave("./final_plots/classification/class_pies_tissue_type.pdf",width = 6, height = 5)

##Figure 3B - Distibution for each tissue type


classification_tissue %>% group_by(dist_category) %>% count()

ordered_names_distr <- classification_tissue %>% 
  filter(dist_category %in% c("detected in single", "detected in some", "detected in many", "detected in all")) %>% 
  separate_rows(tissues_detected, sep = ";") %>% 
  group_by(dist_category, tissues_detected) %>% 
  count() %>% 
  group_by(tissues_detected) %>% 
  summarise(sum = sum(n)) %>% 
  arrange(sum) %>% 
  .$tissues_detected %>% 
  str_to_sentence()

detection_palette <- c("Detected in all" = "#253494",
                        "Detected in many" = "#2c7fb8",
                        "Detected in some" = "#41b6c4",
                        "Detected in single" = "#a1dab4",
                        "Not detected " = "grey")


classification_tissue %>%
  filter(
    dist_category %in% c(
      "detected in single",
      "detected in some",
      "detected in many",
      "detected in all"
    )
  ) %>%
  separate_rows(tissues_detected, sep = ";") %>%
  group_by(dist_category, tissues_detected) %>%
  count() %>%
  mutate(tissues_detected = factor(str_to_sentence( tissues_detected), ordered_names_distr)) %>%
  mutate(dist_category = factor(
    str_to_sentence( dist_category),
    c(
      "Detected in single",
      "Detected in some",
      "Detected in many",
      "Detected in all"
    )
  )) %>%
  ggplot(aes(x = n, y = tissues_detected, fill = dist_category)) +
  geom_col() +
  scale_fill_manual(values = detection_palette) +
  theme_classic() + theme(
    axis.title.y = element_blank(),
    axis.line.y = element_blank(),
    axis.title.x = element_blank(),
    legend.position = "bottom",
    legend.title = element_blank()
  ) +
  scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
  ggtitle("Number of detected genes per tissue type")  +
  guides(fill = guide_legend(nrow = 2, byrow = TRUE))

 ggsave("./final_plots/classification/tissue_detection_a.pdf", height = 11.5, width = 4.5)

NA
NA

##Figure 3C - Distribution and Tau score

detection_palette <- c("Detected in all" = "#253494",
                        "Detected in many" = "#2c7fb8",
                        "Detected in some" = "#41b6c4",
                        "Detected in single" = "#a1dab4",
                        "Not detected " = "grey")

p1 <- classification_tissue %>% 
  mutate(dist_category = factor(str_to_sentence( dist_category), levels = rev(names(detection_palette))),
         enriched_tissues = str_to_sentence(enriched_tissues)) %>% 
  filter(dist_category != "Not detected") %>% 
  ggplot(aes(x = tau_score, y = dist_category, fill = dist_category)) +
  geom_violin() +
  scale_fill_manual(values = gene_category_pal, name = "Specificity") +
  xlab("Tau score") +
  theme_bw() +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
    axis.title.y = element_blank(), 
    axis.text.y = element_blank(), 
    axis.ticks.y = element_blank()
    ) 

#ggsave("./final_plots/classification/tau_to_distribution.pdf",width = 5.5, height = 4)


p2 <- classification_tissue %>%
  ggplot(aes(tau_score)) +
  geom_histogram(bins = 100) +
  theme_classic() +
  ylab("Count")+
  theme(panel.background = element_rect("gray90"),
        #axis.title.y = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank()) +
  scale_y_continuous(expand = expansion(mult = 0, add = 0))

p2/ p1 + plot_layout(heights = c(1, 3))

ggsave("./final_plots/classification/distribution_and_dendrogram_tissue_w_overall.pdf",width = 6.5, height = 6)

###Extra Figures not in report

detection_palette <- c("detected in all" = "#253494",
                        "detected in many" = "#2c7fb8",
                        "detected in some" = "#41b6c4",
                        "detected in single" = "#a1dab4",
                        "not detected " = "grey")

ordered_names_distr <- classification_tissue %>%  
  filter(dist_category %in% c("detected in single"#,
                             # "detected in some"
                              )) %>% 
  separate_rows(tissues_detected, sep = ";") %>% 
  group_by(dist_category, tissues_detected) %>% 
  count() %>% 
  group_by(tissues_detected) %>% 
  summarise(sum = sum(n)) %>% 
  arrange(sum) %>% 
  .$tissues_detected

classification_tissue %>%  
  filter(
    dist_category %in% c(
      "detected in single"#,
#
    )
  ) %>%
  separate_rows(tissues_detected, sep = ";") %>%
  group_by(dist_category, tissues_detected) %>%
  count() %>%
  mutate(tissues_detected = factor(tissues_detected, ordered_names_distr)) %>%
  mutate(dist_category = factor(
    dist_category,
    c(
      "detected in single",
      "detected in some",
      "detected in many",
      "detected in all"
    )
  )) %>%
  ggplot(aes(x = n, y = tissues_detected, fill = dist_category)) +
  geom_col() +
  scale_fill_manual(values = detection_palette) +
  theme_classic() + theme(
    axis.title.y = element_blank(),
    axis.line.y = element_blank(),
    axis.title.x = element_blank(),
    legend.position = "bottom",
    legend.title = element_blank()
  ) +
  scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
#  ggtitle("Number of detected genes per tissue type")  +
  ggtitle("Number of detected genes detected in a single tissue")  +
  guides(fill = guide_legend(nrow = 2, byrow = TRUE))

detection_palette <- c("detected in all" = "#253494",
                        "detected in many" = "#2c7fb8",
                        "detected in some" = "#41b6c4",
                        "detected in single" = "#a1dab4",
                        "not detected " = "grey")

ordered_names_distr <- classification_tissue %>%  
  filter(dist_category %in% c("detected in single",
                              "detected in some"
                              )) %>% 
  separate_rows(tissues_detected, sep = ";") %>% 
  group_by(dist_category, tissues_detected) %>% 
  count() %>% 
  group_by(tissues_detected) %>% 
  summarise(sum = sum(n)) %>% 
  arrange(sum) %>% 
  .$tissues_detected

classification_tissue %>%  
  filter(
    dist_category %in% c(
      "detected in single",
      "detected in some"
    )
  ) %>%
  separate_rows(tissues_detected, sep = ";") %>%
  group_by(dist_category, tissues_detected) %>%
  count() %>%
  mutate(tissues_detected = factor(tissues_detected, ordered_names_distr)) %>%
  mutate(dist_category = factor(
    dist_category,
    c(
      "detected in single",
      "detected in some",
      "detected in many",
      "detected in all"
    )
  )) %>%
  ggplot(aes(x = n, y = tissues_detected, fill = dist_category)) +
  geom_col() +
  scale_fill_manual(values = detection_palette) +
  theme_classic() + theme(
    axis.title.y = element_blank(),
    axis.line.y = element_blank(),
    axis.title.x = element_blank(),
    legend.position = "bottom",
    legend.title = element_blank()
  ) +
  scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
  ggtitle("Number of detected genes per tissue type")  +
 # ggtitle("Number of detected genes detected in a single tissue")  +
  guides(fill = guide_legend(nrow = 2, byrow = TRUE))

#Figure 4 ##Figure 4A - Pie charts at grouped tissue level

gene_category_pal <- c("Detected in all" = "#253494",
                       "Detected in many" = "#2c7fb8",
                       "Detected in some" = "#41b6c4",
                       "Detected in single" = "#a1dab4",
                       "Not detected " = "  #bebebe",
                       "Low tissue specificity" = "grey40",
                       "Tissue enhanced" = "#984ea3",
                       "Group enriched" = "#FF9D00",
                       "Tissue enriched" = "#e41a1c")

plot_data <-
  classification_consensus %>% 
  rename(specificity = spec_category, distribution = dist_category) %>% 
  select(gene, specificity, distribution) %>% 
  gather(class_type, class, -1) %>% 
  group_by(class_type, class) %>% 
  count() %>%
  group_by(class_type) %>% 
  mutate(perc = 100 * n / sum(n),
         label = paste0(n, "\n", round(perc, 1), "%")) %>% 
  ungroup() %>% 
  mutate(class = factor(str_to_sentence(class), str_to_sentence(c("tissue enriched", "group enriched",  "tissue enhanced", "low tissue specificity", "detected in all", "detected in many", "detected in some", "detected in single", "not detected"))),
         class_type = factor(str_to_sentence(class_type),
                             c("Specificity", "Distribution")))
    
plot_dodge = 0.1
plot_data %>% 
  arrange(match(class, rev(levels(class)))) %>% 
  group_by(class_type) %>% 
  mutate(y_stack = cumsum(n) - n/2) %>% 
  {ggplot(., aes(1, n, fill = class, group = class, 
                 label = label)) +
      geom_col(show.legend = F, 
               color = "white", 
               width = 1) + 
      geom_segment(aes(x = 1.5 + plot_dodge, xend = 1.5, 
                       y = y_stack, yend = y_stack), size = 0.5) +
      geom_text_repel(aes(x = 1.5 + plot_dodge, y = y_stack), 
                      color = "black", nudge_x = plot_dodge, 
                      segment.size = 0.5, size = 24/11) +
      scale_fill_manual(values = gene_category_pal) + 
      facet_wrap(~class_type) +
      coord_polar("y",start = 0) +
      theme_void() + 
      scale_x_continuous(expand = expansion(c(0,0.8)))}


ggsave("./final_plots/classification/class_pies_grouped_tissue.pdf",width = 6, height = 5)

##Figure 4B - Speceficity for each grouped tissue

classification_consensus %>% group_by(spec_category) %>% count() 

ordered_names_sp <-
  classification_consensus %>%
  filter(spec_category %in% c("group enriched", "tissue enriched", "tissue enhanced")) %>%
  separate_rows(enriched_tissues, sep = ";") %>%
  group_by(spec_category, enriched_tissues) %>%
  count() %>%
  ungroup() %>% 
  group_by(enriched_tissues) %>%
  summarise(sum = sum(n)) %>%
  ungroup() %>% 
  arrange(sum) %>%
  .$enriched_tissues %>% 
  str_to_sentence()
  
  
  

specificity_palette <- rev(c("Not detected" = "grey",
                           "Low tissue specificity" = "grey40",
                           "Tissue enhanced" = "#984ea3",
                           "Group enriched" = "#FF9D00",
                           "Tissue enriched" = "#e41a1c"))

classification_consensus %>%
  filter(spec_category %in% c("group enriched", "tissue enriched", "tissue enhanced")) %>%
  separate_rows(enriched_tissues, sep = ";") %>%
  group_by(spec_category, enriched_tissues) %>%
  count() %>%
  mutate(enriched_tissues = factor(str_to_sentence(enriched_tissues), ordered_names_sp)) %>%
  mutate(spec_category = factor(str_to_sentence(spec_category), c("Tissue enriched", "Group enriched",  "Tissue enhanced"))) %>%
  ggplot(aes(x = n, y = enriched_tissues, fill = spec_category)) +
  geom_col() +
  scale_fill_manual(values = specificity_palette) +
  xlab("Number of genes")+
  theme_classic() + theme(
    axis.title.y = element_blank(),
  #  axis.title.x = element_blank(),
    axis.line.y = element_blank(),
    legend.position = "bottom",
    legend.title = element_blank()
  ) +
  scale_x_continuous(expand = expansion(mult = 0, add = 0)) +
  ggtitle("Specificity category per consensus tissue") 

ggsave("./final_plots/classification/consensus_tissue_specificity.pdf", height = 7, width = 5.5)

##Figure 4C - Specificity and Tau score



specificity_palette <- rev(c("Not detected" = "grey",
                           "Low tissue specificity" = "grey40",
                           "Tissue enhanced" = "#984ea3",
                           "Group enriched" = "#FF9D00",
                           "Tissue enriched" = "#e41a1c"))

p1 <- classification_consensus %>% 
  mutate(spec_category = factor(str_to_sentence( spec_category), levels = names(specificity_palette)),
         enriched_tissues = str_to_sentence(enriched_tissues)) %>% 
  filter(spec_category != "Not detected") %>% 
  ggplot(aes(x = tau_score, y = spec_category, fill = spec_category)) +
  geom_violin() +
  scale_fill_manual(values = gene_category_pal, name = "Specificity") +
  xlab("Tau score") +
  theme_bw() +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
    axis.title.y = element_blank(), 
    axis.text.y = element_blank(), 
    axis.ticks.y = element_blank()
    ) 

#ggsave("./final_plots/classification/tau_to_specificity.pdf",width = 5.5, height = 4)


p2 <- classification_consensus %>%
  ggplot(aes(tau_score)) +
  geom_histogram(bins = 100) +
  theme_classic() +
  ylab("Count")+
  theme(panel.background = element_rect("gray90"),
        #axis.title.y = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank()) +
  scale_y_continuous(expand = expansion(mult = 0, add = 0))

p2/ p1 + plot_layout(heights = c(1, 3))

ggsave("./final_plots/classification/specificity_and_dendrogram_consensus_w_overall.pdf",width = 7, height = 6)

##Figure 4D - Gene annotation alluvial plot

width = 0.1

alluv_1 <-
  
  classification_consensus %>%
  mutate(Specificity = str_to_sentence(spec_category),
          Distribution = str_to_sentence(dist_category)) %>%
  select(Specificity,
         Distribution) %>%
  mutate(row_n = row_number()) %>%
  gather(bar, chunk, -row_n) %>%
  mutate(color_vars = 1) %>%
  group_by(row_n) %>%
  mutate(chunk_color = chunk[match(c("Specificity",
                                     "Distribution")[color_vars], bar)]) %>% 
  ungroup() %>%
  
  
  mutate(chunk = factor(chunk, levels = c('Tissue enriched', 'Group enriched', 
                                          'Tissue enhanced', 'Low tissue specificity', 
                                          'Detected in single',
                                          'Detected in some', 
                                          'Detected in many', 
                                          'Detected in all',
                                          'Not detected')),
         bar = factor(bar, levels = c("Specificity",
                                      "Distribution"))) %>%
  
  
  ggplot(aes(x = bar, stratum = chunk, alluvium = row_n,
             y = 1)) +
  
  geom_flow(aes(fill = chunk_color), 
            show.legend = F, width = width,
            knot.pos = 1/6) +
  geom_stratum(aes(fill = chunk), 
               show.legend = F, color = NA, width = width) +
  
  scale_x_discrete(expand = c(.1, .1), position = "top") +
  scale_fill_manual(values = c(gene_category_pal)) + 
  
  
  theme(axis.text.y = element_text(size = 18, face = "bold"),
        axis.text.x = element_blank(), 
        axis.ticks = element_blank(), 
        panel.background = element_blank(), 
        axis.title = element_blank()) +
  coord_flip()

# alluv_1

flow_data <-
  ggplot_build(alluv_1)$data[[1]] %>%
  as_tibble() %>%
  {
    if ("side" %in% names(.)) {
      .
    } else{
      mutate(.,
             side = case_when(flow == "from" ~ "start",
                              flow == "to" ~ "end"))
    }
  }



stratum_data <- 
  ggplot_build(alluv_1)$data[[2]]

flow_data_labels <-
  flow_data %>%
  as_tibble() %>%
  select(x, stratum, group, side, ymin, ymax) %>%
  pivot_wider(names_from = side,
              values_from = c(x, stratum, ymin, ymax)) %>%
  mutate_at(
    c(
      "x_end",
      "ymax_end",
      "ymin_end",
      "x_start",
      "ymax_start",
      "ymin_start"
    ),
    as.numeric
  ) %>%
  group_by(stratum_start, stratum_end, x_start, x_end) %>%
  summarise(
    y_end = (min(ymin_end) + max(ymax_end)) / 2,
    y_start = (min(ymin_start) + max(ymax_start)) / 2,
    size = max(ymax_start) - min(ymin_start)
  )
`summarise()` has grouped output by 'stratum_start', 'stratum_end', 'x_start'. You can override using the `.groups` argument.
alluv_2 <- 
  alluv_1 +
  geom_text(data = flow_data_labels,
            aes(x = x_start + width/2,
                y = y_start, 
                label = size), 
            inherit.aes = F, 
            size = 3,
            angle = -90,
            hjust = 1,
            vjust = 0.5) +
  geom_text(data = flow_data_labels,
            aes(x = x_end - width/2,
                y = y_end, 
                label = size), 
            inherit.aes = F, 
            size = 3, 
            angle = -90,
            hjust = 0,
            vjust = 0.5) +
  
  # Stratum label
  
  geom_text(data = stratum_data %>%
              filter(x == 1),
            aes(x = x - width/2,
                y = y,
                label = stratum),
            size = 4, 
            vjust = 1.5,
            inherit.aes = F) + 
  geom_text(data = stratum_data %>%
              filter(x == 2),
            aes(x = x + width/2,
                y = y,
                label = stratum),
            size = 4, 
            vjust = -0.5,
            inherit.aes = F) + 
  
  geom_text(data = stratum_data,
            aes(x = x, 
                y = y,
                label = ymax - ymin), 
            size = 4, 
            fontface = "bold",
            color = "white",
            inherit.aes = F)


alluv_2
ggsave("./final_plots/classification/alluvial_classification.pdf",width = 8, height = 3)

###Extra figures not in report

organ_colors <- metadata %>% select(consensus_tissue_name, consensus_tissue_color) %>% unique()
pal <-  organ_colors$consensus_tissue_color
pal <- setNames(pal, str_to_sentence(organ_colors$consensus_tissue_name))


specificity_palette <- rev(c("Not detected" = "grey",
                           "Low tissue specificity" = "grey40",
                           "Tissue enhanced" = "#984ea3",
                           "Group enriched" = "#FF9D00",
                           "Tissue enriched" = "#e41a1c"))


class_table_temp <-
  classification_consensus %>%
  select(gene, spec_category, enriched_tissues) %>%
  separate_rows(enriched_tissues, sep = ";") %>%
  mutate(spec_category = factor(str_to_sentence( spec_category), levels = names(specificity_palette)),
         enriched_tissues = str_to_sentence(enriched_tissues))

plot_dendro <-
  tmm_consensus_tissue %>% 
  select(target_id, consensus_tissue_name, tmm) %>% 
  mutate(consensus_tissue_name = str_to_sentence(consensus_tissue_name)) %>%
  spread(target_id, tmm) %>%
  column_to_rownames("consensus_tissue_name")  %>%
  t() %>%
  cor(method = "spearman") %>%
  
  {1 - .} %>%
  as.dist() %>% 
  hclust(method = "average") %T>%
  plot %>%
  dendro_data()

  
  
dendro_plot_data <- 
  left_join(plot_dendro$segments, 
            plot_dendro$labels, 
            by = c("x" = "x", "yend" = "y")) 

left_plot <- 
  dendro_plot_data %>%
  ggplot() +
  geom_segment(aes(x=y, y=x, xend=yend, yend=xend, group = label))+
  geom_rect(aes(xmin=0, ymin=x + 0.5, 
                xmax=-0.02, ymax=xend - 0.5, 
                fill = label), 
            show.legend = F) +
  scale_color_manual(values = pal)+
  scale_fill_manual(values = pal)+
  scale_x_reverse(expand = expansion(0), position = "top")+
  scale_y_continuous(expand = expansion()) +
  xlab("1 - Spearman's rho") +
  
  theme(axis.text.y = element_blank(), 
        axis.title.y = element_blank(), 
        axis.ticks.y = element_blank(),
        plot.margin = unit(c(1,1,1,1), units = "mm"), 
        panel.background = element_blank()) 

right_plot <- 
  class_table_temp %>%
  filter(!is.na(enriched_tissues)) %>%
  group_by(enriched_tissues, spec_category) %>%
  summarise(n_genes = n()) %>%
  ungroup() %>%
  mutate(enriched_tissues = factor(enriched_tissues, levels = plot_dendro$labels$label),
         spec_category = factor(spec_category, names(specificity_palette))) %>%
  ggplot(aes(n_genes, enriched_tissues, fill = spec_category)) +
  geom_col(width = 0.8, size = 0.1) +
  theme_bw() + 
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
  scale_fill_manual(values = gene_category_pal, name = "Specificity") +
  # coord_flip() +
  xlab("Number of genes") +
  
  scale_x_continuous(position = "top", expand = expansion(c(0, 0.1))) + 
  scale_y_discrete(expand = expansion()) +
  
  theme(axis.text.y = element_text(hjust = 0.5), 
        legend.position = c(0.7, 0.5),
        axis.title.y = element_blank(),
        panel.border = element_blank())
`summarise()` has grouped output by 'enriched_tissues'. You can override using the `.groups` argument.
left_plot + right_plot +
  plot_layout(widths = c(0.3, 1))


ggsave("./final_plots/classification/specificity_and_dendrogram_consensus.pdf",width = 7, height = 7)

#Figure 5

##Plot based on Max Karlsson's network plot

classification_network_plot_2 <- 
  function(class_table, gene_col, spec_col, enriched_col, spec_filter, pal, savename, enriched_sep = ";", 
           node_filter_rank = 2, node_filter_show_cat = c("tissue enriched"), 
           node_filter_min = 2, node_filter_n_show = 8, scale_factor = 1) {
    
    enrichment_table <- 
      class_table %>% 
      select(gene = gene_col, 
             spec = spec_col, 
             enriched = enriched_col) %>% 
      filter(spec %in% spec_filter) %>% 
      group_by(enriched, spec) %>% 
      summarise(n_genes = n()) %>%
      ungroup()
    
    net_data <-
      enrichment_table %>% 
      mutate(all_enriched = enriched) %>%
      separate_rows(enriched, sep = enriched_sep) %>%
      group_by(enriched, spec) %>% 
      mutate(rank = rank(-n_genes, ties.method = "min")) %>%
      group_by(all_enriched) %>%
      mutate(any_low_rank = any(rank <= node_filter_rank)) %>%
      ungroup() %>%
      
      filter((spec == node_filter_show_cat | any_low_rank | n_genes >= node_filter_n_show) &
               (spec == node_filter_show_cat | n_genes >= node_filter_min)) %>% 
      mutate(edge_id = paste("enriched:", all_enriched)) %>% 
      arrange(n_genes)
    
    net_edges <- 
      net_data %$% 
      tibble(node1 = enriched, node2 = edge_id, n = n_genes) %>% 
      unique()
    
    g <-
      net_edges %>%
      graph_from_data_frame(directed = FALSE) %>%
      ggraph(layout = "kk") 
    
    link_map <- 
      net_edges %>% 
      gather(node, id, -(3)) %>% 
      mutate(tissue_node = node == "node1", 
             color_id = case_when(tissue_node ~ id, 
                                  grepl(";", id) ~ "Group enriched",
                                  !grepl(";", id) ~ "Tissue enriched"), 
             label = ifelse(tissue_node, color_id, n)) %>%
      select(n, node, id, tissue_node, color_id, label) %>%
      unique()
    
    
    edge_data <- get_edges()(g$data)
    node_data <- 
      get_nodes()(g$data) %>% 
      as_tibble() %>%
      left_join(link_map, 
                by = c("name" = "id")) 
    
    
    fig <- 
      g + 
      geom_edge_arc(aes(width = n), 
                    color = "gray", 
                    strength = 0, 
                    alpha = 0.5,
                    show.legend = F) + 
      scale_edge_alpha_continuous(range = c(0.3, 1)) +
      scale_edge_width_continuous(range = c(1, 3)) +
      
      geom_node_point(data = node_data  %>%
                        filter(!tissue_node),
                      aes(size = log10(n), 
                          fill = color_id), 
                      stroke = 1,
                      # size = 10,
                      shape = 21,
                      show.legend = F)+
      geom_node_point(data = node_data %>%
                        filter(tissue_node),
                      aes(fill = color_id), 
                      stroke = 1,
                      size = 20 * scale_factor,
                      shape = 21,
                      show.legend = F)+
      geom_node_text(data = node_data,
                     aes(label = label),
                     size = 4 * scale_factor) +
      scale_size_continuous(range = c(5, 10) * scale_factor) +
      scale_fill_manual(values = pal) +
      
      theme_void()
    
    ## ----- Save
    
    
    cyto_summary <- 
      net_edges %>% 
      mutate(category = ifelse(!grepl(enriched_sep, node2), "Tissue enriched", "Group enriched"), 
             node_id = unclass(factor(node2)),
             node1 = str_to_sentence(node1), 
             n_sqrt = sqrt(n), 
             str_len = str_length(node1)) %>%
      select(category, node1, node2, node_id, n, n_sqrt, str_len) 
    
    cyto_summary %>% 
      write_delim("cytoscape nodes summary.txt", delim = "\t")
  #    write_delim(savepath(paste(savename, "cytoscape nodes summary.txt")), delim = "\t")
    
    bind_rows(cyto_summary %>% 
                left_join(pal %>% 
                            enframe("node1", "color")) %>% 
                select(node_id = node1, 
                       color) %>% 
                unique() %>%
                mutate(node_type = "Tissue"),
              cyto_summary %>% 
                mutate(color = case_when(category == "Tissue enriched" ~ "#e41a1c",
                                         category == "Group enriched" ~ "#FF9D00"), 
                       node_id = as.character(node_id)) %>%
                select(node_id, color) %>% 
                unique() %>%
                
                mutate(node_type = "Enrichment")) %>%
      mutate(color2 = case_when(node_type == "Enrichment" ~ color,
                                node_type == "Tissue" ~ "#D3D3D3FF"),
             color3 = case_when(node_type == "Enrichment" ~ color,
                                node_type == "Tissue" ~ "#BEBEBEFF")) %>% 
      
      write_delim("cytoscape nodes color.txt", delim = "\t") 
      #write_delim(savepath(paste(savename, "cytoscape nodes color.txt")), delim = "\t") 
    
    bind_rows(cyto_summary %>% 
                select(node_id = node1) %>% 
                mutate(label = node_id) %>%
                unique(),
              cyto_summary %>% 
                mutate(node_id = as.character(node_id), 
                       label = as.character(n)) %>%
                select(node_id, label) %>% 
                unique()) %>% 
      write_delim("cytoscape nodes label whole group.txt",
      #write_delim(savepath(paste(savename, "cytoscape nodes label whole group.txt")), 
                  delim = "\t")
    
    ## ----
    
    fig
  }


organ_colors <- metadata %>% select(consensus_tissue_name, organ_color) %>% unique()
pal1 <-  organ_colors$organ_color
pal1 <- setNames(pal1, organ_colors$consensus_tissue_name)

specificity_palette <- rev(c("Not detected" = "grey",
                             "Tissue" = "grey",
                           "Low tissue specificity" = "grey40",
                           "Tissue enhanced" = "#984ea3",
                           "Group enriched" = "#FF9D00",
                           "Tissue enriched" = "#e41a1c"))

library(influential)

classification_network_plot_2(
  class_table = classification_consensus %>% mutate(spec_category = str_to_sentence(spec_category)),
  gene_col = "gene",
  spec_col = "spec_category",
  enriched_col = "enriched_tissues",
  spec_filter = c("Tissue enriched", "Group enriched"),
  pal = c(pal1, specificity_palette),
  savename = "test_interconsensus",
  enriched_sep = ";",
  node_filter_rank = 2,
  node_filter_show_cat = c("Tissue enriched"),
  node_filter_min = 2,
  node_filter_n_show = 5,
  scale_factor = 0.8
) 
`summarise()` has grouped output by 'enriched'. You can override using the `.groups` argument.Joining, by = "node1"
ggsave("./final_plots/data_presentation/nework_plot2-filter.pdf", height = 20, width = 20)

#Read Comparison data

comp_metadata <- read_csv("./data/final_data/comparison_metadata-init-1-0.csv")
Rows: 124 Columns: 14── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (13): tissue_name, region_tissue_name, consensus_tissue_name, organ_name, tissue_color, region_tissue_color, co...
lgl  (1): regional_tissue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
human_atlas_tissue <-
  read_delim("./data/human_hpa/rna_tissue_consensus.tsv", delim = "\t") %>%
  mutate(source = "hpa_consensus") %>%
  group_by(Gene) %>%
  mutate(nx = case_when(nTPM == 0 ~ 0,
                        T ~ nTPM / sqrt(sd(nTPM)))) %>%
  ungroup() #%>% 
Rows: 1084208 Columns: 4── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): Gene, Gene name, Tissue
dbl (1): nTPM
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
  # # just necessary if read data of multiple different sources, then would make sense to average out samples named the same
  # # just from one source atm, so no need for this.
  # group_by(Tissue, Gene) %>%
  # summarise(nTPM = mean(nTPM, na.rm = T)) %>% 
  # ungroup()

hpa_comp <- human_atlas_tissue %>% left_join(
  comp_metadata %>%
    select(hpa_consensus_name, comparison_tissue_name) %>%
    distinct() %>%
    filter(!is.na(hpa_consensus_name)) %>%
    filter(!is.na(comparison_tissue_name)),
  by = c("Tissue" = "hpa_consensus_name")) %>% 
  filter(!is.na(comparison_tissue_name)) %>% 
  group_by(Gene,comparison_tissue_name) %>% 
  summarise(tmm = max(nTPM),
            nx = max(nx)) %>% 
  ungroup() %>% 
  rename(tissue = comparison_tissue_name)
`summarise()` has grouped output by 'Gene'. You can override using the `.groups` argument.
rat_tissue <-
  read_csv("./data/final_data/final_tmm_tissue_name.csv") %>% 
  # #omit gather, data should be  already in long format
  # gather(sample, tmm,-1) %>%
  group_by(target_id) %>%
  mutate(nx = case_when(tmm == 0 ~ 0,
                        T ~ tmm / sqrt(sd(tmm)))) %>%
  ungroup()
Rows: 2224500 Columns: 3── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): target_id, tissue_name
dbl (1): tmm
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
  

# select tissues and pool tissues that will get compared
rat_tissue_comp <- rat_tissue %>% left_join(comp_metadata %>% 
                                              select(tissue_name, comparison_tissue_name), by = c("tissue_name" = "tissue_name")) %>%
  filter(!is.na(comparison_tissue_name)) %>% 
  group_by(target_id, comparison_tissue_name) %>% 
  summarise(tmm = max(tmm),
            nx = max(nx)) %>% 
  ungroup() %>% 
  rename(tissue = comparison_tissue_name)
`summarise()` has grouped output by 'target_id'. You can override using the `.groups` argument.
#check if all have the same tissue
all(rat_tissue_comp %>% distinct(tissue) == hpa_comp %>% distinct(tissue) )
[1] TRUE

#Read ortholog data

#Select either "HPA" or "ensemble"
ortholog_data_from <- "HPA"
#ortholog_data_from <- "ensemble"

include_many2many <- TRUE
include_one2many <- TRUE

#Only needed for ensembl:
#define ensemble version
version     <- 103
organism_db <- "rnorvegicus_gene_ensembl"
#mart <- useEnsembl ( biomart="genes" , dataset=organism_db , version=version )

if (ortholog_data_from == "HPA"){
  homolog_data <- read.table("./data/human_hpa/kalle/ensembl_rat_ortholog.tsv", sep = "\t", header = TRUE) %>% 
    filter(ensrnog_id %in% rat_tissue$target_id) %>% 
    filter(ensg_id %in% human_atlas_tissue$Gene)
} else if (ortholog_data_from == "ensemble"){
  homolog_data <- getBM( attributes=c( "ensembl_gene_id", "hsapiens_homolog_ensembl_gene", "hsapiens_homolog_orthology_type") , mart=mart ) %>%
    filter(hsapiens_homolog_ensembl_gene != "") %>% 
    filter(ensembl_gene_id %in% rat_tissue$target_id) %>% 
    filter(hsapiens_homolog_ensembl_gene %in% human_atlas_tissue$Gene) %>% 
    rename(ensrnog_id = ensembl_gene_id, ensg_id = hsapiens_homolog_ensembl_gene, ortholog_type = hsapiens_homolog_orthology_type)
}

if (include_many2many == FALSE){
  homolog_data <- homolog_data %>% filter(!ortholog_type == "ortholog_many2many")
}
if (include_one2many == FALSE){
  homolog_data <- homolog_data %>% filter(!ortholog_type == "ortholog_one2many")
}

#Figure 6

comparison_colors_tbl <- rbind(
  comp_metadata %>% select(name = comparison_tissue_name, color = consensus_tissue_color) %>% distinct(), 
  comp_metadata %>% select(name = tissue_name, color = tissue_color) %>%  distinct()
  ) %>% 
  distinct() %>% 
  drop_na() %>% 
  mutate(name = str_to_sentence(name))

pal <- comparison_colors_tbl$color
pal <- setNames(pal, str_to_sentence(comparison_colors_tbl$name))


plot_data1 <- 
  comp_metadata %>% 
  select(tissue_name, comparison_tissue_name, organ_name) %>% 
  unique() %>% 
  drop_na() %>% 
  mutate(tissue_name = str_to_sentence(tissue_name), comparison_tissue_name = str_to_sentence(comparison_tissue_name)) %>% 
  arrange(organ_name, comparison_tissue_name) %>% 
  mutate(plot_order = row_number()) %>% select(-organ_name)

plot_data2 <- 
  plot_data1 %>%
  gather(column, label, -plot_order) %>%
  group_by(label, column) %>% 
  summarise(plot_order = mean(plot_order))  %>%
  ungroup() %>% 
  mutate(label = label,
         column = factor(column,
                         c("tissue_name", 
                           "comparison_tissue_name"
                           )))
`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
plot_data3 <- 
  plot_data1 %>% 
  group_by(comparison_tissue_name) %>% 
  mutate(left_pos = mean(plot_order))

plot_data4 <- 
  plot_data2 %>% 
  left_join(comparison_colors_tbl,
            by = c("label" = "name")) %>% 
  group_by(column, label) %>% 
  summarise(miny = min(plot_order) - 0.5,
            maxy = max(plot_order) + 0.5)
`summarise()` has grouped output by 'column'. You can override using the `.groups` argument.
ggplot() +
  geom_rect(data = plot_data4, 
           aes(xmin = column, xmax = column, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F
         ) +
    geom_rect(data = plot_data4 %>% filter (column == "comparison_tissue_name"), 
          # aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color), 
           aes(xmin = 2, xmax = 2.5, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F
         ) +
    geom_rect(data = plot_data4 %>% filter (column == "tissue_name"), 
          # aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color), 
           aes(xmin = 0.5, xmax = 1, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F, width = 10
         ) +
  geom_segment(
    data = plot_data3,
    aes(
      x = "comparison_tissue_name",
      xend = "tissue_name",
      y = left_pos,
      yend = plot_order,
      color = tissue_name
    ),
    show.legend = F,
    alpha = 0.5,
    size = 2
  )+
  geom_text(
    data = plot_data2 %>% filter(column == "comparison_tissue_name"),
    aes(x = column, y = plot_order, label = label),
    hjust = 0,
    size = 2 * 5 / 6,
    label.padding = unit(0, "mm")
  ) +
  geom_text(
    data = plot_data2 %>% filter(column == "tissue_name"),
    aes(x = column, y = plot_order, label = label),
    hjust = 1,
    size = 2 * 5 / 6,
    show.legend = F,
    label.padding = unit(0, "mm")
  ) +
  # geom_label(
  #   data = plot_data2 %>%
  #     filter(column == "organ_name"),
  #   #aes(x = column, y = plot_order, label = label),
  #   aes(x = column, y = plot_order, label = gsub(" ", "\n", label)),
  #   show.legend = F,
  #   label.size = 0,
  #   hjust = 1,
  #   lineheight = 0.7,
  #   label.padding = unit(0, "mm"),
  #   size = 2 * 5 / 6
  # ) +
  scale_y_reverse() +
  scale_x_discrete(labels  = c("Rat tissue", "Comparison tissue"),position = "top") +
  scale_fill_manual(values = pal) +
  scale_color_manual(values = pal) +
  theme_void() +
  theme(axis.text.x = element_text())
Warning: Ignoring unknown parameters: `width`Warning: Ignoring unknown parameters: `label.padding`Warning: Ignoring unknown parameters: `label.padding`
ggsave("final_plots/comparison/comparison_tissues_rat.pdf", height = 5, width = 4)

comparison_colors_tbl <- rbind(
  comp_metadata %>% select(name = comparison_tissue_name, color = consensus_tissue_color) %>% distinct(), 
  comp_metadata %>% select(name = hpa_consensus_name, color = tissue_color) %>%  distinct()
  ) %>% 
  distinct() %>% 
  drop_na() %>% 
  mutate(name = str_to_sentence(name))

pal <- comparison_colors_tbl$color
pal <- setNames(pal, str_to_sentence(comparison_colors_tbl$name))


plot_data1 <- 
  comp_metadata %>% 
  select(hpa_consensus_name, comparison_tissue_name, organ_name) %>% 
  unique() %>% 
  drop_na() %>% 
  mutate(hpa_consensus_name = str_to_sentence(hpa_consensus_name), comparison_tissue_name = str_to_sentence(comparison_tissue_name)) %>% 
  arrange(organ_name, comparison_tissue_name) %>% 
  mutate(plot_order = row_number()) %>% select(-organ_name)

plot_data2 <- 
  plot_data1 %>%
  gather(column, label, -plot_order) %>%
  group_by(label, column) %>% 
  summarise(plot_order = mean(plot_order))  %>%
  ungroup() %>% 
  mutate(label = label,
         column = factor(column,
                         c("comparison_tissue_name", "hpa_consensus_name"
                           )))
`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
plot_data3 <- 
  plot_data1 %>% 
  group_by(comparison_tissue_name) %>% 
  mutate(left_pos = mean(plot_order))

plot_data4 <- 
  plot_data2 %>% 
  left_join(comparison_colors_tbl,
            by = c("label" = "name")) %>% 
  group_by(column, label) %>% 
  summarise(miny = min(plot_order) - 0.5,
            maxy = max(plot_order) + 0.5)
`summarise()` has grouped output by 'column'. You can override using the `.groups` argument.
ggplot() +
  geom_rect(data = plot_data4, 
           aes(xmin = column, xmax = column, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F
         ) +
    geom_rect(data = plot_data4 %>% filter (column == "hpa_consensus_name"), 
          # aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color), 
           aes(xmin = 2, xmax = 2.5, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F
         ) +
    geom_rect(data = plot_data4 %>% filter (column == "comparison_tissue_name"), 
          # aes(xmin = column, xmax = column, ymin = miny, ymax = maxy, color = color), 
           aes(xmin = 0.5, xmax = 1, ymin = miny, ymax =  maxy, fill = label), 
           show.legend = F, width = 10
         ) +
  geom_segment(
    data = plot_data3,
    aes(
      x = "comparison_tissue_name",
      xend = "hpa_consensus_name",
      y = left_pos,
      yend = plot_order,
      color = hpa_consensus_name
    ),
    show.legend = F,
    alpha = 0.5,
    size = 2
  )+
  geom_text(
    data = plot_data2 %>% filter(column == "hpa_consensus_name"),
    aes(x = column, y = plot_order, label = label),
    hjust = 0,
    size = 2 * 5 / 6,
    label.padding = unit(0, "mm")
  ) +
  geom_text(
    data = plot_data2 %>% filter(column == "comparison_tissue_name"),
    aes(x = column, y = plot_order, label = label),
    hjust = 1,
    size = 2 * 5 / 6,
    show.legend = F,
    label.padding = unit(0, "mm")
  ) +
  # geom_label(
  #   data = plot_data2 %>%
  #     filter(column == "organ_name"),
  #   #aes(x = column, y = plot_order, label = label),
  #   aes(x = column, y = plot_order, label = gsub(" ", "\n", label)),
  #   show.legend = F,
  #   label.size = 0,
  #   hjust = 1,
  #   lineheight = 0.7,
  #   label.padding = unit(0, "mm"),
  #   size = 2 * 5 / 6
  # ) +
  scale_y_reverse() +
  scale_x_discrete(labels  = c("Comparison tissue", "Human Tissue"),position = "top") +
  scale_fill_manual(values = pal) +
  scale_color_manual(values = pal) +
  theme_void() +
  theme(axis.text.x = element_text())
Warning: Ignoring unknown parameters: `width`Warning: Ignoring unknown parameters: `label.padding`Warning: Ignoring unknown parameters: `label.padding`
ggsave("final_plots/comparison/comparison_tissues_human_rev.pdf", height = 5, width = 4)

#Batch Correction


#Do batch correction
#join rat and human tmm data together
joined_atlas_comparison_temp <- rat_tissue_comp %>%
  inner_join(homolog_data, by = c("target_id" = "ensrnog_id")) %>%
  unite(mutual_id, target_id, ensg_id, sep = "_") %>%
  mutate(species = "rat") %>%
  select(mutual_id, tissue, species , tmm) %>%
  bind_rows(
    hpa_comp %>%
      inner_join(homolog_data, by = c("Gene" = "ensg_id")) %>%
      unite(mutual_id, ensrnog_id, Gene, sep = "_") %>%
      mutate(species = "human") %>%
      select(mutual_id, tissue, species , tmm)
  )
  #### if tmm is under 0 then it is equal to 0
  #mutate(tmm = ifelse(tmm < 1, 0, tmm ))

#long table with inf of on tissue and species
joined_atlas_comparison_temp_2 <- joined_atlas_comparison_temp %>% 
  unite(id, tissue, species, sep = "_") %>% 
  separate(id, into = c("tissue", "species"), sep = "_", remove = F) 

#wide table only with tmm
joined_atlas_comparison_temp3_tmm <-
  joined_atlas_comparison_temp_2 %>% 
  select(mutual_id, id, tmm) %>% 
  spread(id, tmm) %>% 
  column_to_rownames("mutual_id") 

#log scale
joined_atlas_comparison_temp3_limma <-
  joined_atlas_comparison_temp3_tmm %>%
  {log10(. + 1)} %>%
  limma::removeBatchEffect(batch = colnames(joined_atlas_comparison_temp3_tmm) %>%
                             str_extract("_.*$"))
#put them together in the long format
joined_atlas_comparison<- 
  joined_atlas_comparison_temp_2 %>%
  left_join(joined_atlas_comparison_temp3_tmm %>% 
              as_tibble(rownames = "mutual_id") %>% 
              gather(id, tmm, -1)) %>% 
  left_join(joined_atlas_comparison_temp3_limma %>% 
              as_tibble(rownames = "mutual_id") %>% 
              gather(id, limma_log1p_tmm, -1)) 
Joining, by = c("mutual_id", "id", "tmm")Joining, by = c("mutual_id", "id")

#Figure 7 ##Figure 7A - Cross species dendrogramm


hclust4RNAseq <- function(df, correlation_method = "spearman"){
  #wide dataframe as input 
  #to get correlation between samples, where rows are genes columns are samples
  #to get correlation between genes across samples, input df with genes as columns
  #can use later for dendogram making: ggdendrogram([hclust4RNAseq_results], rotate = FALSE, size = 10, face = "bold")
  similarity <- cor(df, method=correlation_method, use="pairwise.complete.obs")
  dissimilarity <- 1 - similarity
  hcl <- hclust(as.dist(dissimilarity), "average")
  return (hcl)
} 


organ_colors <- comp_metadata %>% select(comparison_tissue_name, consensus_tissue_color) %>% drop_na() %>% distinct()
pal <-  organ_colors$consensus_tissue_color
pal <- setNames(pal, str_to_sentence(organ_colors$comparison_tissue_name))

shape_def <- c(21, 22)
shape_def <- setNames(shape_def, c("Human", "Rat"))


plot_comp_dendro_data <- joined_atlas_comparison %>% 
  select(mutual_id, id, limma_log1p_tmm) %>% 
  spread(id, limma_log1p_tmm) %>% 
  column_to_rownames("mutual_id") %>% 
  cor(method = "spearman", use="pairwise.complete.obs") %>%
  {1 - .} %>%
  as.dist() %>% 
  hclust(method = "average") %T>%
  plot %>%
  dendro_data()


dendro_plot_data <- 
  left_join(plot_comp_dendro_data$segments, 
            plot_comp_dendro_data$labels, 
            by = c("x" = "x", "yend" = "y")) %>% 
  separate(label, c("tissue", "species"), sep = "_", remove = FALSE) %>% 
  mutate(tissue = str_to_sentence(tissue), species = str_to_sentence(species)) %>% 
  mutate(species = factor(species, c("Human", "Rat")))

left_plot <- 
  dendro_plot_data %>%
  ggplot() +
  geom_segment(aes(x=y, y=x, xend=yend, yend=xend))+
  # geom_rect(aes(xmin=0, ymin=x + 0.5, 
  #               xmax=-0.02, ymax=xend - 0.5, 
  #               fill = tissue), 
  #           show.legend = F) +
  geom_point(aes(
      x = 0,
      y = x,
      shape = species,
      fill = tissue
    ),
    color = "gray25",
    alpha = 1,
    size = 3,
    stroke = 0.7
  ) +
  geom_text(aes(x=-0.02, y=x,
                label = tissue),
            hjust = 0,
            show.legend = F) +
  scale_shape_manual(values = shape_def ) +
# scale_color_manual(values = pal) +
  scale_fill_manual(values = pal, guide = "none") +
   scale_x_reverse(
     expand = expansion(0.5 ), 
     position = "top")+
   scale_y_continuous(expand = expansion(0.01)) +
  xlab("1 - Spearman's rho") +
  
  theme(axis.text.y = element_blank(), 
        axis.title.y = element_blank(), 
        axis.ticks.y = element_blank(),
        legend.title=element_blank(),
        plot.margin = unit(c(1,1,1,1), units = "mm"), 
        panel.background = element_blank()) 
left_plot
ggsave("./final_plots/comparison/comparison_dendrogram.pdf", width = 6.5, height = 9 )

##Figure 7B - Cros species UMAP

library(plotly)
library(ggplotify)
library(geomtextpath)

comp_metadata <- read_csv("./data/final_data/comparison_metadata-init-1-0.csv")
Rows: 124 Columns: 14── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (13): tissue_name, region_tissue_name, consensus_tissue_name, organ_name, tissue_color, region_tissue_color, co...
lgl  (1): regional_tissue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
umap_meta <- comp_metadata %>% select(consensus_tissue_color, organ_color, comparison_tissue_name) %>% filter(comparison_tissue_name != "" ) %>% distinct()
wide_data <- joined_atlas_comparison %>% 
  select(-tissue, -species, -tmm) %>% 
  spread(id, limma_log1p_tmm)
seed <- 42
filter_zero_sd = F
n_epochs = 1000 
n_neighbors = 15

pca_res <-
  wide_data %>%
  #no log1p because limma value is already calculated from log scale value
  column_to_rownames(colnames(wide_data)[1]) %>%
  scale() %>%
  t() %>%
  pca(nPcs = dim(.)[1])

pc_lim <-
  which(pca_res@R2cum > 0.8)[1]

pc_lim_sd <-
  rev(which(pca_res@sDev > 1))[1]

n_neighbors = 15
set.seed(seed)
umap_res <- pca_res@scores[, 1:pc_lim] %>%
  uwot::umap(n_neighbors = n_neighbors,
             n_epochs = n_epochs) %>%
  as_tibble() %>%
  set_names(paste0("UMAP", 1:ncol(.))) %>%
  mutate(sample = rownames(pca_res@scores)) %>%
  select(sample, everything()) %>%
  separate(
    sample,
    into = c("tissue", "species"),
    sep = "_",
    remove = F
  ) %>%
  left_join(umap_meta, by = c("tissue" = "comparison_tissue_name"))

organ_colors <-
  umap_meta %>% select(comparison_tissue_name, consensus_tissue_color) %>% unique()
pal <-  organ_colors$consensus_tissue_color
pal <- setNames(pal, organ_colors$comparison_tissue_name)

shape_def <- c(21, 22)
shape_def <- setNames(shape_def, c("human", "rat"))


# umap_res %>%  ggplot(aes(UMAP1, UMAP2,  color = organ_name)) +
#    geom_point(alpha = 0.8) + scale_color_manual(values = pal)
p <- umap_res %>%  
  ggplot(aes(UMAP1, UMAP2)) +
  geom_textpath(
    aes(label = str_to_sentence(tissue)),
    hjust = 0.5,
    vjust = -0.3,
    color = "gray20"
  ) +
  geom_point(
    aes(fill = tissue, shape = species),
    color = "gray25",
    alpha = 1,
    size = 2,
    stroke = 0.7
  ) +
  guides(fill = "none") +
  scale_fill_manual(values = pal) +
  scale_shape_manual(values = shape_def) +
  theme_classic() + theme(legend.text = element_text(size = 10),
                          #legend.title =element_blank(), 
                          legend.spacing.y = unit(-0.1, 'cm'),
                          legend.spacing.x = unit(-0.01, 'cm')) +
  guides(shape = guide_legend(ncol = 1, byrow = TRUE, title = "Species"))

p





if (include_many2many == TRUE & include_one2many == TRUE) {
  comp_umap_file_name = paste0("./final_plots/comparison/",
                               ortholog_data_from,
                               "_umap.pdf")
} else if (include_many2many == FALSE & include_one2many == TRUE) {
  comp_umap_file_name = paste0("./final_plots/comparison/",
                               ortholog_data_from,
                               "_umap.pdf")
} else if (include_many2many == FALSE &
           include_one2many == FALSE) {
  comp_umap_file_name = paste0("./final_plots/comparison/",
                               ortholog_data_from,
                               "_umap.pdf")
}


ggsave(comp_umap_file_name, height = 5.5, width = 6.5)

##Figure 7C - Cross species hypergeometric test

#Based on Max Karlsson: https://github.com/maxkarlsson/Pig-Atlas/blob/master/scripts/functions_classification.R

library(viridis)
library(ggsci)



class1 <- hpa_gene_classification(data = rat_tissue_comp %>% select(target_id, tissue, tmm), expression_col = "tmm", tissue_col = "tissue", gene_col = "target_id", enr_fold = 4, max_group_n = 5, det_lim = 1) %>% rename(ensrnog_id = gene)
class2 <- hpa_gene_classification(data = hpa_comp %>% select(Gene, tissue, tmm), expression_col = "tmm", tissue_col = "tissue", gene_col = "Gene", enr_fold = 4, max_group_n = 5, det_lim = 1) %>% 
  rename(ensg_id = gene)

gene_col1 <- "ensrnog_id"
gene_col2 <- "ensg_id"
sep <- ";"

class1_long <-
  class1 %>%
  select(gene1 = gene_col1, enriched_tissues) %>%
  mutate(
    enriched_tissues = ifelse(is.na(enriched_tissues),
                              "not enriched",
                              enriched_tissues),
    enriched1 = T
  ) %>%
  separate_rows(enriched_tissues, sep = sep)

class2_long <-
  class2 %>%
  select(gene2 = gene_col2, enriched_tissues) %>%
  mutate(
    enriched_tissues = ifelse(is.na(enriched_tissues),
                              "not enriched",
                              enriched_tissues),
    enriched2 = T
  ) %>%
  separate_rows(enriched_tissues, sep = sep)

tis_ <-
  unique(c(class1_long$enriched_tissues,
           class2_long$enriched_tissues)) %>%
  sort()

gene_orthologs <- homolog_data %>% select(1,2)

overlap_hyper_all <- expand.grid(id1 = tis_,
            id2 = tis_,
            gene = 1:nrow(gene_orthologs)) %>%
  as_tibble() %>%
  left_join(gene_orthologs %>%
              select(gene1 = gene_col1,
                     gene2 = gene_col2) %>%
              mutate(gene = row_number())) %>%
  select(-gene) %>%
  left_join(class1_long,
            by = c("gene1", "id1" = "enriched_tissues")) %>%
  left_join(class2_long,
            by = c("gene2", "id2" = "enriched_tissues")) %>%
  mutate(
    enriched1 = ifelse(is.na(enriched1),
                       F,
                       enriched1),
    enriched2 = ifelse(is.na(enriched2),
                       F,
                       enriched2)
  ) %>%
  group_by(id1, id2, enriched1, enriched2) %>%
  count() %>%
  group_by(id1, id2) %>%
  summarise(
    # q is the number of successes
    q = sum(n[which(enriched1 & enriched2)]),
    
    # k is the number of tries - i.e. the number of genes that are elevated for either species
    k = sum(n[which(enriched1 | enriched2)]),
    
    # m is the number of possible successes - i.e. the number of genes that are elevated for either
    m = min(sum(n[which(enriched1)]),
            sum(n[which(enriched2)])),
    
    # n is the population size - i.e. the number of genes
    n = sum(n) - m
  ) %>% 
  mutate(p_value = phyper(q - 1, m, n, k, lower.tail = F)) %>%
  mutate(p_value = ifelse(p_value == 0, .Machine$double.xmin, p_value),
         adj_pval = p.adjust(p_value, method = "BH")) %>%
  rename(rat_id = id1, 
         human_id = id2)
Joining, by = "gene"`summarise()` has grouped output by 'id1'. You can override using the `.groups` argument.
overlap_hyper_all %>% 
  write_csv("./data/final_data/rat_human_class_hyper.csv")


plot_order <- 
  overlap_hyper_all %>% 
  ungroup() %>%
  mutate(rat_id = str_to_sentence(rat_id),
         human_id = str_to_sentence(human_id)) %>%
  filter(rat_id == human_id) %>%
  arrange(adj_pval) %>%
  pull(rat_id)


stripped_theme <-
  theme(panel.background = element_rect(fill = NA, colour = NA),
        plot.background = element_rect(fill = NA, color = NA),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        legend.key = element_rect(colour = NA),
        #legend.position = "bottom",
        #legend.direction = "horizontal",
        legend.key.size= unit(0.3, "cm"),
        legend.title = element_text(face="italic"),
        axis.line = element_line(colour="black",size=0.5))


overlap_hyper_all %>% 
  group_by(rat_id, human_id) %>%
  mutate(capped_p = min(c(-log10(adj_pval), 20))) %>%
  ungroup() %>% 
  mutate(rat_id = str_to_sentence(rat_id),
         human_id = str_to_sentence(human_id)) %>%
  mutate(rat_id = factor(rat_id, plot_order),
         human_id = factor(human_id, plot_order)) %>%
  ggplot(aes(human_id, rat_id, fill = capped_p)) +
  geom_tile() + 
  geom_tile(data = . %>% 
              filter(adj_pval >= 0.05),
            fill = "black") + 
  scale_fill_viridis(option = "D", direction = 1, 
                     name = "Adjusted p-value") + 
  stripped_theme +
  theme(axis.text.x = element_text(angle = -90, hjust = 0, vjust = 0.2),
        legend.position = "top") +
  xlab("Human") + 
  ylab("Rat")


#ggsave("./final_plots/comparison/Overlap hyper heatmap elevated.pdf", width = 6, height = 6)  

overlap_hyper_all %>% 
  filter(human_id != "not enriched" & 
           rat_id != "not enriched") %>%
  ungroup() %>%
  mutate(rat_id = str_to_sentence(rat_id),
         human_id = str_to_sentence(human_id)) %>%
  filter(adj_pval < 0.05) %>%
  mutate(rat_id = factor(rat_id, plot_order),
         human_id = factor(human_id, plot_order),
         adj_pval = case_when(adj_pval < 1e-100 ~ 1e-100,
                              T ~ adj_pval)) %>%
  ggplot(aes(human_id, rat_id, fill = -log10(adj_pval), size = -log10(adj_pval))) +
  geom_point(shape = 21, 
             alpha = 0.8,
             stroke = 0.2,
             color = "black") + 
  # scale_color_viridis(option = "E", direction = 1, 
  #                    name = "Adjusted p-value") + 
  scale_fill_gradientn(colors = ggsci::rgb_material(palette = "deep-orange")) +
  scale_size_continuous(range = c(1, 6)) +
  stripped_theme +
  theme(axis.text.x = element_text(angle = -90, hjust = 0, vjust = 0.2),
        legend.position = "top", 
        panel.grid.major = element_line(color = "gray90", size = 0.2)) +
  xlab("Human") + 
  ylab("Rat")

ggsave("./final_plots/comparison/hypergeometric_comparison_tissue.pdf", width = 5, height = 5.5)  
Warning: ‘mode(bg)’ differs between new and previous
     ==> NOT changing ‘bg’

##Figure 7D - Cross species gene annotation alluvial


#classification rat
classification_rat_comp <-
  hpa_gene_classification(
    data = joined_atlas_comparison %>% filter(species == "rat") %>% select(c(-id,-species,-limma_log1p_tmm)),
    expression_col = "tmm",
    tissue_col = "tissue",
    gene_col = "mutual_id",
    enr_fold = 4,
    max_group_n = 5,
    det_lim = 1
  )

rat_comp_tau <- calculate_tau_score(
  joined_atlas_comparison %>% filter(species == "rat") %>% select(-id,-species,-limma_log1p_tmm)  %>%
    spread(tissue, tmm) %>%
    mutate_if(is.numeric, function(x) {
      log10(x + 1)
    }) %>%
    column_to_rownames("mutual_id")
)

classification_rat_comp <- classification_rat_comp %>%
  left_join(rat_comp_tau, by = c("gene" = "gene")) %>%
  mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score)) %>%
  mutate(species = "Rat")


#classification human

classification_human_comp <-
  hpa_gene_classification(
    data = joined_atlas_comparison %>% filter(species == "human") %>% select(c(-id,-species,-limma_log1p_tmm)),
    expression_col = "tmm",
    tissue_col = "tissue",
    gene_col = "mutual_id",
    enr_fold = 4,
    max_group_n = 5,
    det_lim = 1
  )

human_comp_tau <- calculate_tau_score(
  joined_atlas_comparison %>% filter(species == "human") %>% select(-id,-species,-limma_log1p_tmm)  %>%
    spread(tissue, tmm) %>%
    mutate_if(is.numeric, function(x) {
      log10(x + 1)
    }) %>%
    column_to_rownames("mutual_id")
)

classification_human_comp <- classification_human_comp %>%
  left_join(rat_comp_tau, by = c("gene" = "gene")) %>%
  mutate(tau_score = ifelse(spec_category == "not detected", NA, tau_score)) %>%
  mutate(species = "Human")
alluvial_data_comp <- classification_rat_comp %>%
  select(gene, spec_category) %>%
  rename(Rat = spec_category) %>%
  left_join(
    classification_human_comp %>%
      select(gene, spec_category) %>%
      rename(Human = spec_category), 
    by = "gene"
  )


width = 0.1

alluv_1 <-
  
  alluvial_data_comp %>%
  mutate(Rat = str_to_sentence(Rat),
          Human = str_to_sentence(Human)) %>%
  select(Rat, 
         Human) %>%
  mutate(row_n = row_number()) %>%
  gather(bar, chunk, -row_n) %>%
  mutate(color_vars = 1) %>%
  group_by(row_n) %>%
  mutate(chunk_color = chunk[match(c("Human",
                                     "Rat")[color_vars], bar)]) %>% 
  ungroup() %>%
  
  
  mutate(chunk = factor(chunk, levels = c('Tissue enriched', 'Group enriched', 
                                          'Tissue enhanced', 'Low tissue specificity', 
                                          'Not detected')),
         bar = factor(bar, levels = c("Human",
                                      "Rat"))) %>%
  
  
  ggplot(aes(x = bar, stratum = chunk, alluvium = row_n,
             y = 1)) +
  
  geom_flow(aes(fill = chunk_color), 
            show.legend = F, width = width,
            knot.pos = 1/6) +
  geom_stratum(aes(fill = chunk), 
               show.legend = F, color = NA, width = width) +
  
  scale_x_discrete(expand = c(.1, .1), position = "top") +
  scale_fill_manual(values = c(gene_category_pal)) + 
  
  
  theme(axis.text.y = element_text(size = 18, face = "bold"),
        axis.text.x = element_blank(), 
        axis.ticks = element_blank(), 
        panel.background = element_blank(), 
        axis.title = element_blank()) +
  coord_flip()

# alluv_1

flow_data <-
  ggplot_build(alluv_1)$data[[1]] %>%
  as_tibble() %>%
  {
    if ("side" %in% names(.)) {
      .
    } else{
      mutate(.,
             side = case_when(flow == "from" ~ "start",
                              flow == "to" ~ "end"))
    }
  }



stratum_data <- 
  ggplot_build(alluv_1)$data[[2]]

flow_data_labels <-
  flow_data %>%
  as_tibble() %>%
  select(x, stratum, group, side, ymin, ymax) %>%
  pivot_wider(names_from = side,
              values_from = c(x, stratum, ymin, ymax)) %>%
  mutate_at(
    c(
      "x_end",
      "ymax_end",
      "ymin_end",
      "x_start",
      "ymax_start",
      "ymin_start"
    ),
    as.numeric
  ) %>%
  group_by(stratum_start, stratum_end, x_start, x_end) %>%
  summarise(
    y_end = (min(ymin_end) + max(ymax_end)) / 2,
    y_start = (min(ymin_start) + max(ymax_start)) / 2,
    size = max(ymax_start) - min(ymin_start)
  )
`summarise()` has grouped output by 'stratum_start', 'stratum_end', 'x_start'. You can override using the `.groups` argument.
alluv_2 <- 
  alluv_1 +
  geom_text(data = flow_data_labels,
            aes(x = x_start + width/2,
                y = y_start, 
                label = size), 
            inherit.aes = F, 
            size = 3,
            angle = -90,
            hjust = 1,
            vjust = 0.5) +
  geom_text(data = flow_data_labels,
            aes(x = x_end - width/2,
                y = y_end, 
                label = size), 
            inherit.aes = F, 
            size = 3, 
            angle = -90,
            hjust = 0,
            vjust = 0.5) +
  
  # Stratum label
  
  geom_text(data = stratum_data %>%
              filter(x == 1),
            aes(x = x - width/2,
                y = y,
                label = stratum),
            size = 4, 
            vjust = 1.5,
            inherit.aes = F) + 
  geom_text(data = stratum_data %>%
              filter(x == 2),
            aes(x = x + width/2,
                y = y,
                label = stratum),
            size = 4, 
            vjust = -0.5,
            inherit.aes = F) + 
  
  geom_text(data = stratum_data,
            aes(x = x, 
                y = y,
                label = ymax - ymin), 
            size = 4, 
            fontface = "bold",
            color = "white",
            inherit.aes = F)


alluv_2

if (include_many2many == TRUE & include_one2many == TRUE){
  comp_alluv_file_name = paste0("./final_plots/comparison/",ortholog_data_from,"_comparison_all_alluvial.pdf")
} else if (include_many2many == FALSE & include_one2many == TRUE){
  comp_alluv_file_name = paste0("./final_plots/comparison/",ortholog_data_from,"_comparison_only_on2one2many_alluvial.pdf")
} else if (include_many2many == FALSE & include_one2many == FALSE){
  comp_alluv_file_name = paste0("./final_plots/comparison/",ortholog_data_from,"_comparison_only_one2one_alluvial.pdf")}

ggsave(comp_alluv_file_name,width = 8, height = 3)

#Figure S1 - Brain Metadata alluvial plot

#Function adapted from Max Karlsson
multi_alluvial_plot <-
  function(data,
           vars,
           chunk_levels,
           pal,
           color_by = c(1, 3, 3)) {
    selvars = vars
    
    if (!is.null(names(vars))) {
      vars = names(vars)
    }
    
    alluv_1 <-
      data %>%
      ungroup() %>%
      select(selvars) %>%
      ungroup() %>%
      mutate(row_n = row_number()) %>%
      gather(bar, chunk,-row_n) %>%
      left_join(tibble(bar = vars,
                       color_vars = color_by),
                by = "bar") %>%
      group_by(row_n) %>%
      mutate(chunk_color = chunk[match(vars[color_vars], bar)]) %>%
      ungroup() %>%
      
      mutate(chunk = factor(chunk, levels = chunk_levels),
             bar = factor(bar, levels = vars)) %>%
      
      
      ggplot(aes(
        x = bar,
        stratum = chunk,
        alluvium = row_n,
        y = 1
      )) +
      
      geom_flow(aes(fill = chunk_color),
                show.legend = F) +
      geom_stratum(aes(fill = chunk),
                   show.legend = F, color = NA) +
      
      scale_x_discrete(expand = c(.1, .1), position = "top") +
       scale_fill_manual(values = pal) +

      theme(
        axis.text.x = element_text(size = 18, face = "bold"),
        axis.text.y = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_blank(),
        axis.title = element_blank()
      )

    
    
    flow_data <-
      ggplot_build(alluv_1)$data[[1]] %>%
      as_tibble() %>%
      {
        if ("side" %in% names(.)) {
          .
        } else{
          mutate(.,
                 side = case_when(flow == "from" ~ "start",
                                  flow == "to" ~ "end"))
        }
      }
    
    
    stratum_data <-
      ggplot_build(alluv_1)$data[[2]]
    
    flow_data_labels <-
      flow_data %>%
      as_tibble() %>%
      
      select(x, stratum, group, side, ymin, ymax) %>%
      pivot_wider(names_from = side,
                  values_from = c(x, stratum, ymin, ymax)) %>%
      
      mutate_at(
        c(
          "x_end",
          "ymax_end",
          "ymin_end",
          "x_start",
          "ymax_start",
          "ymin_start"
        ),
        as.numeric
      ) %>%
      group_by(stratum_start, stratum_end, x_start, x_end) %>%
      summarise(
        y_end = (min(ymin_end) + max(ymax_end)) / 2,
        y_start = (min(ymin_start) + max(ymax_start)) / 2,
        size = max(ymax_start) - min(ymin_start)
      )
    
    alluv_1 <-
      alluv_1 +
      # geom_text(
      #   data = flow_data_labels,
      #   aes(x = x_start + 1 / 6,
      #       y = y_start,
      #       label = size),
      #   inherit.aes = F,
      #   size = 3,
      #   hjust = 0
      # ) +
      # geom_text(
      #   data = flow_data_labels,
      #   aes(x = x_end - 1 / 6,
      #       y = y_end,
      #       label = size),
      #   inherit.aes = F,
      #   size = 3,
      #   hjust = 1
      # ) +
      # 
      # Stratum label
      geom_text(
        data = stratum_data,
        aes(
          x = x,
          y = y,
          label = paste(stratum#, 
                       # paste("[", ymax - ymin, "]", sep = "")
                        )
        ),
        size = 4,
        inherit.aes = F
      )
                
    
    alluv_1
  }
metadata_b <- metadata %>% filter(organ_name =="Brain")

t_names <- metadata_b$tissue_name %>% unique()
r_names <- metadata_b$region_tissue_name %>% unique()
c_names <- metadata_b$consensus_tissue_name %>% unique()
o_names <- metadata_b$organ_name %>% unique()

t_colors <- metadata_b %>% select(tissue_name, tissue_color) %>% unique() %>% rename (chunk = tissue_name, color = tissue_color)
r_colors <- metadata_b %>% select(region_tissue_name, region_tissue_color) %>% unique() %>% rename (chunk = region_tissue_name, color = region_tissue_color)
c_colors <- metadata_b %>% select(consensus_tissue_name, consensus_tissue_color) %>% unique() %>% rename (chunk = consensus_tissue_name, color = consensus_tissue_color)
o_colors <- metadata_b %>% select(organ_name, organ_color) %>% unique() %>% rename (chunk = organ_name, color = organ_color)

bind_colors <- bind_rows(t_colors, r_colors, o_colors) %>% unique() %>% arrange(chunk) %>% mutate(row_n = row_number())
pal <-  bind_colors$color
pal <- setNames(pal, bind_colors$chunk)

data = metadata_b
vars = c("tissue_name", "region_tissue_name", "organ_name")
chunk_levels = c(t_names, r_names, o_names) %>% unique()
color_by = c(1, 3, 3)

multi_alluvial_plot(data = metadata_b, vars = vars, chunk_levels = chunk_levels, pal = pal, color_by = c(1, 3, 3))
`summarise()` has grouped output by 'stratum_start', 'stratum_end', 'x_start'. You can override using the `.groups` argument.
ggsave("final_plots/alluvial/brain-tco-1_p.pdf", height = 7, width = 10)

#Figure S2 - Normalisation comparison TPM vs nTPM (TMM)

tpm_sample <-read_csv("./data/final_data/curated_pTPM_rattus_norvegicus_v103.csv")
Rows: 22245 Columns: 353── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (1): target_id
dbl (352): ventral.forebrain_male.3, ventral.forebrain_male.2, ventral.forebrain_female.3, ventral.forebrain_female...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
sample_subset_ID <- metadata[match(unique(metadata$consensus_tissue_name), metadata$consensus_tissue_name),] %>% select(ID)
sample_subset_tmm <- tmm_sample %>% select(target_id, sample_subset_ID$ID) %>% gather(sample, tmm, -1)
sample_subset_tpm <- tpm_sample %>% select(target_id, sample_subset_ID$ID) %>% gather(sample, tpm, -1)

# plot_data <- left_join(sample_subset_tmm, sample_subset_tpm, by = c("target_id", "sample")) %>%
#   mutate(log1p_tmm = log10(tmm + 1), log1p_tpm = log10(tpm +1)) %>% select(-tmm, -tpm) %>% 
#   gather(expression_type, expression, -1, -2) %>% 
#   left_join(metadata %>% select(ID, tissue_name, consensus_tissue_name), by = c("sample" = "ID"))

plot_data <- left_join(sample_subset_tmm, sample_subset_tpm, by = c("target_id", "sample")) %>%
  gather(expression_type, expression, -1, -2) %>% mutate_if(is.numeric, function(x) {
                log10(x + 1)
              }) %>% 
  left_join(metadata %>% select(ID, tissue_name, consensus_tissue_name), by = c("sample" = "ID"))


pal_tbl <- metadata %>% select(consensus_tissue_name, consensus_tissue_color) %>% distinct()
pal <- pal_tbl %>% pull(consensus_tissue_color)
pal <- setNames(pal, pal_tbl %>% pull(consensus_tissue_name))


ggplot(data = plot_data, aes(x = expression, y =sample, fill = consensus_tissue_name)) +
  geom_boxplot(draw_quantiles = 0.5, outlier.size = 0.5, outlier.alpha = 0.3)+
  facet_wrap(~expression_type)+
  scale_fill_manual(values = pal) +
  theme(legend.position = "none", 
        axis.title = element_blank())
Warning: Ignoring unknown parameters: `draw_quantiles`
ggsave("./final_plots/tpm_tmm_comp_boxplot.pdf", width=7, height = 12)

#Figure S3 - Spearman heatmap (tissye type level)


##Spearman's roh heatmap at tissue type level
if(file.exists("./data/final_data/spearman_corr_tissues.csv")) {
  tissue_tmm_spearman <- read_csv("./data/final_data/spearman_corr_tissues.csv")
} else {
  tissue_tmm_spearman <-  tmm_tissue %>%
    spread(tissue_name, tmm) %>%
    column_to_rownames("target_id") %>%
    cor(method = "spearman", use = "pairwise.complete.obs") %>%
    as.data.frame() %>%
    as_tibble(rownames = "tissue_name")
  write_csv(as.data.frame(tissue_tmm_spearman) %>% as_tibble(rownames = "tissue_name"),"./data/final_data/spearman_corr_tissues.csv")
}
Rows: 100 Columns: 101── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (1): tissue_name
dbl (100): abdominal adipose tissue, adrenal gland, amygdala, aorta, artery, bone marrow, breast, bronchus, brown a...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
tissue_tmm_spearman %>% 
  column_to_rownames("tissue_name") %>% 
  pheatmap(
   # clustering_method = "ward.D2",
    cellheight = 8,
    cellwidth = 8, 
    border_color = NA,
    color = viridis::inferno(20, direction = -1),
    show_rownames = FALSE, 
    ) %>% 
  as.ggplot()


ggsave("./final_plots/data_presentation/spearman_corr_tissue.pdf", height = 20, width = 20)

NA
NA

#Figure S4 - Comparison Pheatmap

if(file.exists("./data/final_data/spearman_corr_tissues.csv")) {
  tissue_tmm_spearman <- read_csv("./data/final_data/spearman_corr_tissues.csv")
} else {
  tissue_tmm_spearman <-  tmm_tissue %>%
    spread(tissue_name, tmm) %>%
    column_to_rownames("target_id") %>%
    cor(method = "spearman", use = "pairwise.complete.obs") %>%
    as.data.frame() %>%
    as_tibble(rownames = "tissue_name")
  write_csv(as.data.frame(tissue_tmm_spearman) %>% as_tibble(rownames = "tissue_name"),"./data/final_data/spearman_corr_tissues.csv")
}
Rows: 100 Columns: 101── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (1): tissue_name
dbl (100): abdominal adipose tissue, adrenal gland, amygdala, aorta, artery, bone marrow, breast, bronchus, brown a...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
joined_atlas_comparison_pheat_data <-  joined_atlas_comparison %>%
  select(mutual_id, id,limma_log1p_tmm ) %>%  
  spread(id, limma_log1p_tmm) %>% 
  column_to_rownames("mutual_id") %>% 
  cor(method = "spearman", use = "pairwise.complete.obs") %>%
  as.data.frame() %>%
  as_tibble(rownames = "tissue_name")

joined_atlas_comparison_pheat_data %>% 
  column_to_rownames("tissue_name") %>% 
  pheatmap(
   # clustering_method = "ward.D2",
    cellheight = 8,
    cellwidth = 8, 
    border_color = NA,
    color = viridis::inferno(20, direction = -1),
    show_rownames = FALSE, 
    ) %>% 
  as.ggplot()


ggsave("./final_plots/comparison/pheatmap.pdf", width = 20, height = 20)

sessionInfo()
R version 4.2.1 (2022-06-23)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Ventura 13.1

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggsci_2.9           geomtextpath_0.1.1  plotly_4.10.1       influential_2.2.6   magrittr_2.0.3     
 [6] viridis_0.6.2       viridisLite_0.4.1   igraph_1.3.5        patchwork_1.1.2     ggraph_2.1.0       
[11] ggrepel_0.9.2       ggplotify_0.1.0     pheatmap_1.0.12     uwot_0.1.14.9000    Matrix_1.5-3       
[16] ggdendro_0.1.23     ggalluvial_0.12.3   forcats_0.5.2       stringr_1.5.0       dplyr_1.0.10       
[21] purrr_0.3.5         readr_2.1.3         tidyr_1.2.1         tibble_3.1.8        ggplot2_3.4.0      
[26] tidyverse_1.3.2     biomaRt_2.52.0      pcaMethods_1.88.0   Biobase_2.56.0      BiocGenerics_0.42.0

loaded via a namespace (and not attached):
  [1] readxl_1.4.1           backports_1.4.1        BiocFileCache_2.4.0    systemfonts_1.0.4     
  [5] plyr_1.8.8             lazyeval_0.2.2         sp_1.5-1               splines_4.2.1         
  [9] NOISeq_2.40.0          GenomeInfoDb_1.32.4    digest_0.6.31          yulab.utils_0.0.5     
 [13] htmltools_0.5.4        fansi_1.0.3            memoise_2.0.1          googlesheets4_1.0.1   
 [17] cluster_2.1.4          tzdb_0.3.0             limma_3.52.4           Biostrings_2.64.1     
 [21] graphlayouts_0.8.4     modelr_0.1.10          vroom_1.6.0            timechange_0.1.1      
 [25] prettyunits_1.1.1      colorspace_2.0-3       blob_1.2.3             rvest_1.0.3           
 [29] rappdirs_0.3.3         textshaping_0.3.6      haven_2.5.1            xfun_0.35             
 [33] crayon_1.5.2           RCurl_1.98-1.9         jsonlite_1.8.4         glue_1.6.2            
 [37] polyclip_1.10-4        gtable_0.3.1           gargle_1.2.1           zlibbioc_1.42.0       
 [41] XVector_0.36.0         kernlab_0.9-31         prabclus_2.3-2         DEoptimR_1.0-11       
 [45] scales_1.2.1           DBI_1.1.3              Rcpp_1.0.9             progress_1.2.2        
 [49] gridGraphics_0.5-1     bit_4.0.5              mclust_6.0.0           stats4_4.2.1          
 [53] htmlwidgets_1.5.4      httr_1.4.4             FNN_1.1.3.1            RColorBrewer_1.1-3    
 [57] fpc_2.2-9              modeltools_0.2-23      ellipsis_0.3.2         pkgconfig_2.0.3       
 [61] XML_3.99-0.13          flexmix_2.3-18         farver_2.1.1           sass_0.4.4            
 [65] nnet_7.3-18            dbplyr_2.2.1           utf8_1.2.2             labeling_0.4.2        
 [69] tidyselect_1.2.0       rlang_1.0.6            AnnotationDbi_1.58.0   munsell_0.5.0         
 [73] cellranger_1.1.0       tools_4.2.1            cachem_1.0.6           cli_3.4.1             
 [77] generics_0.1.3         RSQLite_2.2.19         broom_1.0.1            evaluate_0.19         
 [81] fastmap_1.1.0          ragg_1.2.4             yaml_2.3.6             knitr_1.41            
 [85] bit64_4.0.5            fs_1.5.2               tidygraph_1.2.2        robustbase_0.95-0     
 [89] KEGGREST_1.36.3        xml2_1.3.3             compiler_4.2.1         rstudioapi_0.14       
 [93] filelock_1.0.2         curl_4.3.3             png_0.1-8              reprex_2.0.2          
 [97] tweenr_2.0.2           bslib_0.4.1            stringi_1.7.8          lattice_0.20-45       
[101] vctrs_0.5.1            pillar_1.8.1           lifecycle_1.0.3        jquerylib_0.1.4       
[105] data.table_1.14.6      irlba_2.3.5.1          bitops_1.0-7           R6_2.5.1              
[109] gridExtra_2.3          IRanges_2.30.1         MASS_7.3-58.1          assertthat_0.2.1      
[113] withr_2.5.0            S4Vectors_0.34.0       GenomeInfoDbData_1.2.8 diptest_0.76-0        
[117] parallel_4.2.1         hms_1.1.2              grid_4.2.1             class_7.3-20          
[121] rmarkdown_2.18         googledrive_2.0.0      ggforce_0.4.1          lubridate_1.9.0       
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ2FsbHV2aWFsKQpsaWJyYXJ5KGJpb21hUnQpCmxpYnJhcnkoZ2dkZW5kcm8pCmxpYnJhcnkocGNhTWV0aG9kcykKbGlicmFyeSh1d290KQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KGdncGxvdGlmeSkKbGlicmFyeSAoZ2dyZXBlbCkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkocGF0Y2h3b3JrKQoKCgpzZWxlY3QgPC0gZHBseXI6OnNlbGVjdApgYGAKCmBgYHtyfQp0bW1fc2FtcGxlIDwtIHJlYWRfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9jdXJhdGVkX3BUTU1fcmF0dHVzX25vcnZlZ2ljdXNfdjEwMy5jc3YiKQptZXRhZGF0YSA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvY3VyYXRlZF9tZXRhZGF0YS5jc3YiKQphbGwoc29ydChjb2xuYW1lcyh0bW1fc2FtcGxlWy0xXSkpID09IHNvcnQobWV0YWRhdGEkSUQpKQoKYGBgCgojR2VuZXJhdGUgaGllcmFyY2hpY2FsIGRhdGEKYGBge3J9CgoKI3Nsb3csIGJ1dCB1bmRlcnN0YW5hYmxlCnRtbV90aXNzdWUgPC0gdG1tX3NhbXBsZSAlPiUgCiAgZ2F0aGVyKHNhbXBsZSwgdG1tLCAtMSkgJT4lIAogIGxlZnRfam9pbihtZXRhZGF0YSAlPiUgc2VsZWN0KElELHRpc3N1ZV9uYW1lKSwgYnkgPSBjKCJzYW1wbGUiID0gIklEIikpICU+JSAKICBncm91cF9ieSh0YXJnZXRfaWQsIHRpc3N1ZV9uYW1lKSAlPiUgCiAgc3VtbWFyaXplKHRtbSA9IG1lYW4odG1tKSkgJT4lIAogIHVuZ3JvdXAoKQoKd3JpdGVfY3N2KHRtbV90aXNzdWUsIGZpbGU9Ii4vZGF0YS9maW5hbF9kYXRhL2ZpbmFsX3RtbV90aXNzdWVfbmFtZS5jc3YiKQoKdG1tX3JlZ2lvbl90aXNzdWUgPC0gdG1tX3Rpc3N1ZSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhICU+JSBzZWxlY3QodGlzc3VlX25hbWUscmVnaW9uX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJ0aXNzdWVfbmFtZSIgPSAidGlzc3VlX25hbWUiKSkgJT4lIAogIGdyb3VwX2J5KHRhcmdldF9pZCwgcmVnaW9uX3Rpc3N1ZV9uYW1lKSAlPiUgCiAgc3VtbWFyaXplKHRtbSA9IG1heCh0bW0pKSAlPiUgCiAgdW5ncm91cCgpCgp3cml0ZV9jc3YodG1tX3JlZ2lvbl90aXNzdWUsIGZpbGU9Ii4vZGF0YS9maW5hbF9kYXRhL2ZpbmFsX3RtbV9yZWdpb25fdGlzc3VlX25hbWUuY3N2IikKCnRtbV9jb25zZW5zdXNfdGlzc3VlIDwtIHRtbV9yZWdpb25fdGlzc3VlICU+JSAKICBsZWZ0X2pvaW4obWV0YWRhdGEgJT4lIHNlbGVjdChyZWdpb25fdGlzc3VlX25hbWUsY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJyZWdpb25fdGlzc3VlX25hbWUiID0gInJlZ2lvbl90aXNzdWVfbmFtZSIpKSAlPiUgCiAgZ3JvdXBfYnkodGFyZ2V0X2lkLCBjb25zZW5zdXNfdGlzc3VlX25hbWUpICU+JSAKICBzdW1tYXJpemUodG1tID0gbWF4KHRtbSkpICU+JSAKICB1bmdyb3VwKCkKCndyaXRlX2Nzdih0bW1fY29uc2Vuc3VzX3Rpc3N1ZSwgZmlsZT0iLi9kYXRhL2ZpbmFsX2RhdGEvZmluYWxfdG1tX2NvbnNlbnN1c19uYW1lLmNzdiIpCgojY2hlY2sKYWxsKG1ldGFkYXRhJHRpc3N1ZV9uYW1lICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpID09IHRtbV90aXNzdWUkdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpICU+JSBzb3J0KCkpCmFsbChtZXRhZGF0YSRyZWdpb25fdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpICU+JSBzb3J0KCkgPT0gdG1tX3JlZ2lvbl90aXNzdWUkcmVnaW9uX3Rpc3N1ZV9uYW1lICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpKQphbGwobWV0YWRhdGEkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpID09IHRtbV9jb25zZW5zdXNfdGlzc3VlJGNvbnNlbnN1c190aXNzdWVfbmFtZSAlPiUgdW5pcXVlKCkgJT4lIHNvcnQoKSkKYGBgCgpgYGB7cn0KIyAjIGZhc3RlciwgYnV0IGxlc3MgdW5kZXJzdGFuZGFibGUKIyB1bmlxdWVfdGlzc3VlIDwtIHNlbGVjdChtZXRhZGF0YSx0aXNzdWVfbmFtZSkgJT4lCiMgICBkaXN0aW5jdCgpCiMgdG1tX2J5X3Rpc3N1ZSA8LSB0aWJibGUodGFyZ2V0X2lkID0gdG1tX3NhbXBsZSR0YXJnZXRfaWQpCiMgZm9yIChpIGluIHVuaXF1ZV90aXNzdWUkdGlzc3VlX25hbWUpIHsKIyAgIGN1cmVudF90aXNzdWVfbWV0YSA9IG1ldGFkYXRhW21ldGFkYXRhJHRpc3N1ZV9uYW1lID09IGksXQojICAgY3VycmVudF90bW0gPSBzZWxlY3QodG1tX3NhbXBsZSxjKHRhcmdldF9pZCwgY3VyZW50X3Rpc3N1ZV9tZXRhJElEKSkKIyAgIG1lYW5zIDwtIGN1cnJlbnRfdG1tICU+JSBzZWxlY3QoY3VyZW50X3Rpc3N1ZV9tZXRhJElEKSAlPiUgcm93TWVhbnMoKQojICAgdG1tX2J5X3Rpc3N1ZVt0b1N0cmluZyhpKV0gPC0gbWVhbnMKIyB9CiMgI3RtbV9ieV90aXNzdWUgPC0gdG1tX2J5X3Rpc3N1ZSAlPiUgZ2F0aGVyKHRpc3N1ZV9uYW1lLCB0bW0sIC0xKQojICN3cml0ZS5jc3YodG1tX2J5X3Rpc3N1ZSwgZmlsZT0idG1tX3Rpc3N1ZV9uYW1lLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQojIAojIHVuaXF1ZV9yZWdpb24gPC0gc2VsZWN0KG1ldGFkYXRhLHJlZ2lvbl90aXNzdWVfbmFtZSkgJT4lCiMgICBkaXN0aW5jdCgpCiMgdG1tX2J5X3JlZ2lvbiA8LSB0aWJibGUodGFyZ2V0X2lkID0gdG1tX3NhbXBsZSR0YXJnZXRfaWQpCiMgZm9yIChpIGluIHVuaXF1ZV9yZWdpb24kcmVnaW9uX3Rpc3N1ZV9uYW1lKSB7CiMgICBjdXJyZW50X3JlZ2lvbl9tZXRhID0gbWV0YWRhdGFbbWV0YWRhdGEkcmVnaW9uX3Rpc3N1ZV9uYW1lID09IGksXQojICAgY3VycmVudF90bW0gPSBzZWxlY3QodG1tX2J5X3Rpc3N1ZSwgYyh0YXJnZXRfaWQsIHVuaXF1ZShjdXJyZW50X3JlZ2lvbl9tZXRhJHRpc3N1ZV9uYW1lKSkpCiMgICBtYXggPC0gc2VsZWN0KGN1cnJlbnRfdG1tLC10YXJnZXRfaWQpICU+JSBhcHBseSgxLG1heCkKIyAgIHRtbV9ieV9yZWdpb25bdG9TdHJpbmcoaSldIDwtIG1heAojIH0KIyAKIyAjIHdyaXRlLmNzdih0bW1fYnlfcmVnaW9uLCBmaWxlPSJ0bW1fcmVnaW9uX25hbWUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCiMgCiMgdW5pcXVlX2NvbnNlbnN1cyA8LSBzZWxlY3QobWV0YWRhdGEsY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSAlPiUKIyAgIGRpc3RpbmN0KCkKIyB0bW1fYnlfY29uc2Vuc3VzIDwtIHRpYmJsZSh0YXJnZXRfaWQgPSB0bW1fc2FtcGxlJHRhcmdldF9pZCkKIyBmb3IgKGkgaW4gdW5pcXVlX2NvbnNlbnN1cyRjb25zZW5zdXNfdGlzc3VlX25hbWUpIHsKIyAgIGN1cnJlbnRfY29uc2Vuc3VzX21ldGEgPSBtZXRhZGF0YVttZXRhZGF0YSRjb25zZW5zdXNfdGlzc3VlX25hbWUgPT0gaSxdCiMgICBjdXJyZW50X3RtbSA9IHNlbGVjdCh0bW1fYnlfcmVnaW9uLCBjKHRhcmdldF9pZCwgdW5pcXVlKGN1cnJlbnRfY29uc2Vuc3VzX21ldGEkcmVnaW9uX3Rpc3N1ZV9uYW1lKSkpCiMgICBtYXggPC0gc2VsZWN0KGN1cnJlbnRfdG1tLC10YXJnZXRfaWQpICU+JSBhcHBseSgxLG1heCkKIyAgIHRtX2J5X2NvbnNlbnN1c1t0b1N0cmluZyhpKV0gPC0gbWF4CiMgfQoKI3dyaXRlLmNzdih0bW1fYnlfY29uc2Vuc3VzLCBmaWxlPSJ0bW1fY29uc2Vuc3VzX25hbWUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKI0ZpZ3VyZSAxCiMjRmlndXJlIDFBIC0gSGllcmFyY2h5IE92ZXJ2aWV3CmBgYHtyfQp0aXNzdWVfY29sb3JzX3BhbGV0dGVfZnVsbCA8LSByYmluZCgKICBtZXRhZGF0YSAlPiUgc2VsZWN0KG5hbWUgPSB0aXNzdWVfbmFtZSwgY29sb3IgPSB0aXNzdWVfY29sb3IpLCAKICBtZXRhZGF0YSAlPiUgc2VsZWN0KG5hbWUgPSBjb25zZW5zdXNfdGlzc3VlX25hbWUsIGNvbG9yID0gY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvciksCiAgbWV0YWRhdGEgJT4lIHNlbGVjdChuYW1lID0gb3JnYW5fbmFtZSwgY29sb3IgPSBvcmdhbl9jb2xvcikKKSAlPiUgZGlzdGluY3QoKSAlPiUgCiAgbXV0YXRlKG5hbWUgPSBzdHJfdG9fc2VudGVuY2UobmFtZSkpICU+JSBhcnJhbmdlKG5hbWUpCgpwYWwgPC0gdGlzc3VlX2NvbG9yc19wYWxldHRlX2Z1bGwkY29sb3IKcGFsIDwtIHNldF9uYW1lcyhwYWwsdGlzc3VlX2NvbG9yc19wYWxldHRlX2Z1bGwkbmFtZSApCgoKcGxvdF9kYXRhMSA8LSAKICBtZXRhZGF0YSAlPiUKICBzZWxlY3QodGlzc3VlX25hbWUsIHJlZ2lvbl90aXNzdWVfbmFtZSwgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lLCBvcmdhbl9uYW1lKSAlPiUgIywKICAgICAgICAgI3Rpc3N1ZV9jb2xvciwgcmVnaW9uX3Rpc3N1ZV9jb2xvciwgY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvciwgb3JnYW5fY29sb3IpICU+JSAKICBtdXRhdGUodGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlX25hbWUpLCByZWdpb25fdGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UocmVnaW9uX3Rpc3N1ZV9uYW1lKSwgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lID0gc3RyX3RvX3NlbnRlbmNlKGNvbnNlbnN1c190aXNzdWVfbmFtZSksIG9yZ2FuX25hbWUgPSBzdHJfdG9fc2VudGVuY2UoIG9yZ2FuX25hbWUpKSAlPiUgICB1bmlxdWUoKSAlPiUgCiAgbXV0YXRlKG9yZ2FuX25hbWUgPSBmYWN0b3IoY2FzZV93aGVuKG9yZ2FuX25hbWUgPT0gIk1hbGUgcmVwcm9kdWN0aXZlIHN5c3RlbSIgfiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWFsZSB0aXNzdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5fbmFtZSA9PSAiQnJlYXN0IGFuZCBmZW1hbGUgcmVwcm9kdWN0aXZlIHN5c3RlbSIgfgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGZW1hbGUgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuX25hbWUgPT0gIkFkaXBvc2UgJiBzb2Z0IHRpc3N1ZSIgfiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29ubmVjdGl2ZSAmIHNvZnQgdGlzc3VlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5fbmFtZSA9PSAiQm9uZSBtYXJyb3cgJiBpbW11bmUgc3lzdGVtIiB+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJvbmUgbWFycm93ICYgbHltcGhvaWQgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFQgfiBvcmdhbl9uYW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJCcmFpbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRXllIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvY3JpbmUgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVzcGlyYXRvcnkgc3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcm94aW1hbCBkaWdlc3RpdmUgdHJhY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdhc3Ryb2ludGVzdGluYWwgdHJhY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxpdmVyICYgZ2FsbGJsYWRkZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktpZG5leSAmIHVyaW5hcnkgYmxhZGRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGFuY3JlYXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1hbGUgdGlzc3VlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmVtYWxlIHRpc3N1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk11c2NsZSB0aXNzdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb25uZWN0aXZlICYgc29mdCB0aXNzdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNraW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJvbmUgbWFycm93ICYgbHltcGhvaWQgdGlzc3VlcyIpKSkKCnBsb3RfZGF0YTEgPC0gcGxvdF9kYXRhMSAlPiUgCiAgYXJyYW5nZShvcmdhbl9uYW1lLAogICAgICAgICAgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lLAogICAgICAgICAgcmVnaW9uX3Rpc3N1ZV9uYW1lLAogICAgICAgICAgdGlzc3VlX25hbWUpICU+JSAKICBtdXRhdGUocGxvdF9vcmRlciA9IHJvd19udW1iZXIoKSkKCnBsb3RfZGF0YTIgPC0gCiAgcGxvdF9kYXRhMSAlPiUKICBzZWxlY3QoLXJlZ2lvbl90aXNzdWVfbmFtZSkgJT4lCiAgZ2F0aGVyKGNvbHVtbiwgbGFiZWwsIC1wbG90X29yZGVyKSAlPiUKICBncm91cF9ieShsYWJlbCwgY29sdW1uKSAlPiUgCiAgc3VtbWFyaXNlKHBsb3Rfb3JkZXIgPSBtZWFuKHBsb3Rfb3JkZXIpKSAgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUobGFiZWwgPSBsYWJlbCwKICAgICAgICAgY29sdW1uID0gZmFjdG9yKGNvbHVtbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGMoIm9yZ2FuX25hbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbnNlbnN1c190aXNzdWVfbmFtZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAidGlzc3VlX25hbWUiKSkpCgpwbG90X2RhdGEzIDwtIAogIHBsb3RfZGF0YTEgJT4lIAogIGdyb3VwX2J5KGNvbnNlbnN1c190aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShsZWZ0X3BvcyA9IG1lYW4ocGxvdF9vcmRlcikpCgpwbG90X2RhdGE0IDwtIAogIHBsb3RfZGF0YTIgJT4lIAogIGxlZnRfam9pbih0aXNzdWVfY29sb3JzX3BhbGV0dGVfZnVsbCwKICAgICAgICAgICAgYnkgPSBjKCJsYWJlbCIgPSAibmFtZSIpKSAlPiUgCiAgZ3JvdXBfYnkoY29sdW1uLCBsYWJlbCkgJT4lIAogIHN1bW1hcmlzZShtaW55ID0gbWluKHBsb3Rfb3JkZXIpIC0gMC41LAogICAgICAgICAgICBtYXh5ID0gbWF4KHBsb3Rfb3JkZXIpICsgMC41KQoKCmdncGxvdCgpICsKICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQsIAogICAgICAgICAgIGFlcyh4bWluID0gY29sdW1uLCB4bWF4ID0gY29sdW1uLCB5bWluID0gbWlueSwgeW1heCA9ICBtYXh5LCBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYKICAgICAgICAgKSArCiAgICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQgJT4lIGZpbHRlciAoY29sdW1uID09ICJ0aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMywgeG1heCA9IDQsIHltaW4gPSBtaW55LCB5bWF4ID0gIG1heHksIGZpbGwgPSBsYWJlbCksIAogICAgICAgICAgIHNob3cubGVnZW5kID0gRgogICAgICAgICApICsKICAgIGdlb21fcmVjdChkYXRhID0gcGxvdF9kYXRhNCAlPiUgZmlsdGVyIChjb2x1bW4gPT0gImNvbnNlbnN1c190aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMSwgeG1heCA9IDIsIHltaW4gPSBtaW55LCB5bWF4ID0gIG1heHksIGZpbGwgPSBsYWJlbCksIAogICAgICAgICAgIHNob3cubGVnZW5kID0gRiwgd2lkdGggPSAxMAogICAgICAgICApICsKICBnZW9tX3NlZ21lbnQoCiAgICBkYXRhID0gcGxvdF9kYXRhMywKICAgIGFlcygKICAgICAgeCA9ICJjb25zZW5zdXNfdGlzc3VlX25hbWUiLAogICAgICB4ZW5kID0gInRpc3N1ZV9uYW1lIiwKICAgICAgeSA9IGxlZnRfcG9zLAogICAgICB5ZW5kID0gcGxvdF9vcmRlciwKICAgICAgY29sb3IgPSB0aXNzdWVfbmFtZQogICAgKSwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGFscGhhID0gMC41LAogICAgc2l6ZSA9IDIKICApKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBwbG90X2RhdGEyICU+JSBmaWx0ZXIoY29sdW1uID09ICJjb25zZW5zdXNfdGlzc3VlX25hbWUiKSwKICAgIGFlcyh4ID0gY29sdW1uLCB5ID0gcGxvdF9vcmRlciwgbGFiZWwgPSBsYWJlbCksCiAgICBoanVzdCA9IDEsCiAgICBzaXplID0gMiAqIDUgLyA2LAogICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIikKICApICsKICBnZW9tX3RleHQoCiAgICBkYXRhID0gcGxvdF9kYXRhMiAlPiUgZmlsdGVyKGNvbHVtbiA9PSAidGlzc3VlX25hbWUiKSwKICAgIGFlcyh4ID0gY29sdW1uLCB5ID0gcGxvdF9vcmRlciwgbGFiZWwgPSBsYWJlbCksCiAgICBoanVzdCA9IDAsCiAgICBzaXplID0gMiAqIDUgLyA2LAogICAgc2hvdy5sZWdlbmQgPSBGLAogICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIikKICApICsKICBnZW9tX2xhYmVsKAogICAgZGF0YSA9IHBsb3RfZGF0YTIgJT4lCiAgICAgIGZpbHRlcihjb2x1bW4gPT0gIm9yZ2FuX25hbWUiKSwKICAgICNhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgYWVzKHggPSBjb2x1bW4sIHkgPSBwbG90X29yZGVyLCBsYWJlbCA9IGdzdWIoIiAiLCAiXG4iLCBsYWJlbCkpLAogICAgc2hvdy5sZWdlbmQgPSBGLAogICAgbGFiZWwuc2l6ZSA9IDAsCiAgICBoanVzdCA9IDEsCiAgICBsaW5laGVpZ2h0ID0gMC43LAogICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIiksCiAgICBzaXplID0gMiAqIDUgLyA2CiAgKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzICA9IGMoJ09yZ2FuIHN5c3RlbScsICJHcm91cGVkIHRpc3N1ZSIsICJUaXNzdWUgdHlwZSIpLHBvc2l0aW9uID0gInRvcCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCgpKQpnZ3NhdmUoImZpbmFsX3Bsb3RzL3Rpc3N1ZXMucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSA0KQoKYGBgCgojI0ZpZ3VyZSAxQiAtIFdhcmQncyBSZXRpbmEtRGVuZHJvZ3JhbW0KYGBge3J9CgojI0Z1bmN0aW5vIGJhc2VkIG9uIE1heCBLYXJsc3NvbidzIHJldGlub2dyYW1tCmNpcmN1bGFyX2RlbmRyb2dyYW1fcmV0aW5hc3R5bGVfMiA8LQogIGZ1bmN0aW9uKGNsdXN0LCBjb2xvcl9tYXBwaW5nLCBsYWJlbF9jb2wsIGNvbG9yX2NvbCwgCiAgICAgICAgICAgc2NhbGVfZXhwYW5zaW9uID0gYygwLjI1LCAwLjI1KSwgdGV4dF9zaXplID0gMywgd2lkdGhfcmFuZ2UgPSBjKDEuNSwgNiksIAogICAgICAgICAgIGFyY19zdHJlbmd0aCA9IDAuOCwgZGVmYXVsdF9jb2xvciA9ICJncmF5ODAiKSB7CiAgICByZXF1aXJlKGdncmFwaCkKICAgIHJlcXVpcmUoaWdyYXBoKQogICAgcmVxdWlyZSh2aXJpZGlzKQogICAgcmVxdWlyZSh0aWR5dmVyc2UpCiAgICByZXF1aXJlKG1hZ3JpdHRyKQogICAgCiAgICBkZW5kcm9ncmFtIDwtCiAgICAgIGNsdXN0ICU+JQogICAgICBhcy5kZW5kcm9ncmFtKCkKICAgIAogICAgCiAgICAKICAgIGcgPC0KICAgICAgZ2dyYXBoKGRlbmRyb2dyYW0sIGxheW91dCA9ICdkZW5kcm9ncmFtJywgY2lyY3VsYXIgPSBUKQogICAgIyAKICAgICMgZyArCiAgICAjICAgZ2VvbV9lZGdlX2ZhbihkYXRhID0gZWRnZV9kYXRhICU+JQogICAgIyAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGhnaGwgPSBlZGdlLmlkID09IDk5KSwKICAgICMgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBlZGdlX2lkLCBjb2xvciA9IGFzLmZhY3RvcihyYW5rX3JhZGl1cykpLAogICAgIyAgICAgICAgICAgICAgICAgIHdpZHRoID00KSArCiAgICAjICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbGFiZWwpKQogICAgCiAgICBlZGdlX2RhdGEgPC0gCiAgICAgIGdldF9lZGdlcygpKGckZGF0YSkgJT4lCiAgICAgIGFzX3RpYmJsZSgpICU+JQogICAgICBsZWZ0X2pvaW4oY29sb3JfbWFwcGluZyAlPiUKICAgICAgICAgICAgICAgICAgc2VsZWN0KGxhYmVsID0gbGFiZWxfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvcl9jb2wpLAogICAgICAgICAgICAgICAgYnkgPSBjKCJub2RlMi5sYWJlbCIgPSAibGFiZWwiKSkgJT4lCiAgICAgIG11dGF0ZShyYWRpdXMgPSB4ZW5kXjIgKyB5ZW5kXjIsCiAgICAgICAgICAgICBlZGdlLmlkID0gYXMuY2hhcmFjdGVyKGVkZ2UuaWQpKSAlPiUKICAgICAgYXJyYW5nZSgtcmFkaXVzKSAlPiUKICAgICAgbXV0YXRlKGVkZ2VfaWQgPSBhcy5jaGFyYWN0ZXIocm93X251bWJlcigpKSwKICAgICAgICAgICAgIHJhbmtfcmFkaXVzID0gdW5jbGFzcyhmYWN0b3IoLXJhZGl1cykpLAogICAgICAgICAgICAgeF9tID0gcm91bmQoeCwgMTApLAogICAgICAgICAgICAgeV9tID0gcm91bmQoeSwgMTApLAogICAgICAgICAgICAgeGVuZF9tID0gcm91bmQoeGVuZCwgMTApLAogICAgICAgICAgICAgeWVuZF9tID0gcm91bmQoeWVuZCwgMTApKSAKICAgIAogICAgCiAgICBlZGdlX2lkX2NvbG9ycyA8LSAKICAgICAgZWRnZV9kYXRhICU+JSAKICAgICAgZmlsdGVyKCFpcy5uYShjb2xvcikpICUkJQogICAgICBzZXRfbmFtZXMoY29sb3IsIGVkZ2VfaWQpCiAgICAKICAgIAogICAgZm9yKHJhbmtfcmFkIGluIDI6bWF4KGVkZ2VfZGF0YSRyYW5rX3JhZGl1cykpIHsKICAgICAgZWRnZV9pZF9jb2xvcnNfbmV3IDwtIAogICAgICAgIGxlZnRfam9pbihlZGdlX2RhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGVkZ2VfaWQsIHJhZGl1cywgeGVuZF9tLCB5ZW5kX20sIHJhbmtfcmFkaXVzKSAlPiUKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIocmFua19yYWRpdXMgPT0gcmFua19yYWQpLAogICAgICAgICAgICAgICAgICBlZGdlX2RhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGVkZ2VfaWQsIHJhZGl1cywgeF9tLCB5X20sIHJhbmtfcmFkaXVzKSAlPiUKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIocmFua19yYWRpdXMgPCByYW5rX3JhZCksCiAgICAgICAgICAgICAgICAgIGJ5ID0gYygieGVuZF9tIiA9ICJ4X20iLCAieWVuZF9tIiA9ICJ5X20iKSkgJT4lCiAgICAgICAgbGVmdF9qb2luKGVuZnJhbWUoZWRnZV9pZF9jb2xvcnMpLAogICAgICAgICAgICAgICAgICBieSA9IGMoImVkZ2VfaWQueSIgPSAibmFtZSIpKSAlPiUKICAgICAgICBncm91cF9ieShlZGdlX2lkLngpICU+JSAKICAgICAgICBzdW1tYXJpc2UoY29sb3IgPSBpZmVsc2Uobl9kaXN0aW5jdCh2YWx1ZSkgPT0gMSAmIGFueSh2YWx1ZSAhPSBkZWZhdWx0X2NvbG9yKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3Rlcih1bmlxdWUodmFsdWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdF9jb2xvcikpICUkJQogICAgICAgIHNldF9uYW1lcyhjb2xvciwgZWRnZV9pZC54KQogICAgICBlZGdlX2lkX2NvbG9ycyA8LSAKICAgICAgICBjKGVkZ2VfaWRfY29sb3JzLCBlZGdlX2lkX2NvbG9yc19uZXcpCiAgICB9CiAgICAKICAgIAogICAgCiAgICBnICsKICAgICAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IHdpZHRoX3JhbmdlKSsKICAgICAgZ2VvbV9lZGdlX2RpYWdvbmFsKGRhdGEgPSBlZGdlX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZWRnZV9jb2xvciA9IGFzLmNoYXJhY3RlcihlZGdlX2lkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX3dpZHRoID0gMSAtIHNxcnQoeGVuZF4yICsgeWVuZF4yKSksCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHJlbmd0aCA9IGFyY19zdHJlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikgKwogICAgICAKICAgICAgCiAgICAgIHNjYWxlX2VkZ2VfY29sb3JfbWFudWFsKHZhbHVlcyA9IGVkZ2VfaWRfY29sb3JzKSAgKwogICAgICBnJGRhdGEgJT4lCiAgICAgIGZpbHRlcihsYWJlbCAhPSAiIikgJT4lCiAgICAgIG11dGF0ZShkZWdyZWUgPSBjYXNlX3doZW4oeCA+PSAwIH4gYXNpbih5KSAqIDE4MCAvIHBpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPCAwIH4gMzYwIC0gYXNpbih5KSAqIDE4MCAvIHBpKSkgJT4lCiAgICAgIGxlZnRfam9pbihjb2xvcl9tYXBwaW5nICU+JQogICAgICAgICAgICAgICAgICBzZWxlY3QobGFiZWwgPSBsYWJlbF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yX2NvbCksCiAgICAgICAgICAgICAgICBieSA9ICJsYWJlbCIpICU+JQogICAgICAgICAgICAgICAge2dlb21fbm9kZV90ZXh0KGRhdGEgPSAuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IC4kZGVncmVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gaWZlbHNlKC4keCA8IDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSB0ZXh0X3NpemUpfSAgKwogICAgICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5kX3NjYWxlKHNjYWxlX2V4cGFuc2lvbikpICsKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuZF9zY2FsZShzY2FsZV9leHBhbnNpb24pKSArCiAgICAgIAogICAgICBjb29yZF9maXhlZCgpICsKICAgICAgdGhlbWVfdm9pZCgpCiAgfQpgYGAKCgpgYGB7cn0KCmhjbHVzdDRSTkFzZXFfd2FyZCA8LSBmdW5jdGlvbihkZiwgY29ycmVsYXRpb25fbWV0aG9kID0gInNwZWFybWFuIil7CiAgI3dpZGUgZGF0YWZyYW1lIGFzIGlucHV0IAogICN0byBnZXQgY29ycmVsYXRpb24gYmV0d2VlbiBzYW1wbGVzLCB3aGVyZSByb3dzIGFyZSBnZW5lcyBjb2x1bW5zIGFyZSBzYW1wbGVzCiAgI3RvIGdldCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGdlbmVzIGFjcm9zcyBzYW1wbGVzLCBpbnB1dCBkZiB3aXRoIGdlbmVzIGFzIGNvbHVtbnMKICAjY2FuIHVzZSBsYXRlciBmb3IgZGVuZG9ncmFtIG1ha2luZzogZ2dkZW5kcm9ncmFtKFtoY2x1c3Q0Uk5Bc2VxX3Jlc3VsdHNdLCByb3RhdGUgPSBGQUxTRSwgc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKQogIHNpbWlsYXJpdHkgPC0gY29yKGRmLCBtZXRob2Q9Y29ycmVsYXRpb25fbWV0aG9kLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCiAgZGlzc2ltaWxhcml0eSA8LSAxIC0gc2ltaWxhcml0eQogIGhjbCA8LSBoY2x1c3QoYXMuZGlzdChkaXNzaW1pbGFyaXR5KSwgIndhcmQuRDIiKQogIHJldHVybiAoaGNsKQp9IAoKdGlzc3VlX2RlbmRyb193YXJkIDwtIGhjbHVzdDRSTkFzZXFfd2FyZCh0bW1fdGlzc3VlICU+JSBtdXRhdGUodGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlX25hbWUpKSAlPiUgIHNwcmVhZCh0aXNzdWVfbmFtZSwgdG1tKSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJ0YXJnZXRfaWQiKSkKCmNpcmN1bGFyX2RlbmRyb2dyYW1fcmV0aW5hc3R5bGVfMigKICBjbHVzdCA9IHRpc3N1ZV9kZW5kcm9fd2FyZCwgCiAgY29sb3JfbWFwcGluZyA9IG1ldGFkYXRhICU+JSAKICAgIHNlbGVjdCh0aXNzdWVfbmFtZSwgdGlzc3VlX2NvbG9yKSAlPiUgCiAgICBtdXRhdGUodGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlX25hbWUpKSwgCiAgbGFiZWxfY29sID0gInRpc3N1ZV9uYW1lIiwgCiAgY29sb3JfY29sID0gInRpc3N1ZV9jb2xvciIsIAogIHNjYWxlX2V4cGFuc2lvbiA9IGMoMC43LCAwLjcpLCAKICB0ZXh0X3NpemUgPSAyLjQsIAogIHdpZHRoX3JhbmdlID0gYygwLjUsIDQpLAogIGFyY19zdHJlbmd0aCA9IDAuNCwgCiAgZGVmYXVsdF9jb2xvciA9ICJncmF5ODAiKQoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3JldGluYWdyYW1fYWxsX3Rpc3N1ZV9jbHVzdF93YXJkLnBkZiIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNikKCmBgYAoKIyNGaWd1cmUgMUMgLSBTcGVhcm1hbiBoZWF0bWFwIChncm91cGVkIHRpc3N1ZSkKYGBge3J9CiMjU3BlYXJtYW4ncyByb2ggaGVhdG1hcCBhdCBncm91cGVkIHRpc3N1ZSBsZXZlbAoKaWYoZmlsZS5leGlzdHMoIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfY29uc2Vuc3VzX3Rpc3N1ZXMuY3N2IikpIHsKICBjb25zZW5zdXNfdG1tX3NwZWFybWFuIDwtIHJlYWRfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9zcGVhcm1hbl9jb3JyX2NvbnNlbnN1c190aXNzdWVzLmNzdiIpCn0gZWxzZSB7CiAgY29uc2Vuc3VzX3RtbV9zcGVhcm1hbiA8LSAgdG1tX2NvbnNlbnN1c190aXNzdWUgJT4lIAogICAgc3ByZWFkKGNvbnNlbnN1c190aXNzdWVfbmFtZSwgdG1tKSAlPiUgCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoInRhcmdldF9pZCIpICU+JSAKICAgIGNvcihtZXRob2Q9InNwZWFybWFuIiwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSAlPiUgCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgYXNfdGliYmxlKHJvd25hbWVzID0gImNvbnNlbnN1c190aXNzdWVfbmFtZSIpCiAgd3JpdGVfY3N2KGNvbnNlbnN1c190bW1fc3BlYXJtYW4sIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfY29uc2Vuc3VzX3Rpc3N1ZXMuY3N2IikKfQoKY29uc2Vuc3VzX3RtbV9zcGVhcm1hbiAlPiUgCiAgcmVuYW1lX3dpdGgoc3RyX3RvX3NlbnRlbmNlLCAtMSkgJT4lIAogIG11dGF0ZShjb25zZW5zdXNfdGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UoY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygiY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lIikgJT4lIAogIHBoZWF0bWFwKAogICAjIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLAogICAgY2VsbGhlaWdodCA9IDgsCiAgICBjZWxsd2lkdGggPSA4LCAKICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgY29sb3IgPSB2aXJpZGlzOjppbmZlcm5vKDIwLCBkaXJlY3Rpb24gPSAtMSksCiAgICBzaG93X3Jvd25hbWVzID0gRkFMU0UsIAogICAgKSAlPiUgCiAgYXMuZ2dwbG90KCkKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NwZWFybWFuX2NvcnJfY29uc2Vuc3VzX3Rpc3N1ZS5wZGYiLCBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCkKCmBgYAoKCiNGaWd1cmUgMgojI0ZpZ3VyZSAyQSAtIFNhbXBsZSBsZXZlbCBQQ0EgUGxvdApgYGB7cn0KCnNhbXBsZV9wY2EgPC0KICB0bW1fc2FtcGxlICU+JSAKICAKICAjIGdhdGhlcihzYW1wbGVfbmFtZSwgdG1tLCAtMSkgJT4lCiAgIyBncm91cF9ieSh0YXJnZXRfaWQpICU+JQogICMgbXV0YXRlKHNkID0gc2QodG1tKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lCiAgIyBmaWx0ZXIoc2QgPiAwKSAlPiUKICAjIHNlbGVjdCgtc2QpICU+JQogICMgc3ByZWFkKHNhbXBsZV9uYW1lLCB0bW0pICU+JQogIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJ0YXJnZXRfaWQiKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IDgpCgojc2FtcGxlX3BjYUBzY29yZXMKCnN1bW1hcnkoc2FtcGxlX3BjYSkKCnBsb3RfZGF0YSA8LSBzYW1wbGVfcGNhICU+JSAKICBzY29yZXMoKSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInNhbXBsZV9pZCIpICU+JSAKICBsZWZ0X2pvaW4obWV0YWRhdGEsCiAgICAgICAgICAgIGJ5ID0gYygic2FtcGxlX2lkIiA9ICJJRCIpKQoKCm9yZ2FuX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KG9yZ2FuX25hbWUsIG9yZ2FuX2NvbG9yKSAlPiUgdW5pcXVlKCkKcGFsIDwtICBvcmdhbl9jb2xvcnMkb3JnYW5fY29sb3IKcGFsIDwtIHNldE5hbWVzKHBhbCwgb3JnYW5fY29sb3JzJG9yZ2FuX25hbWUpCgpwbG90X2RhdGEgJT4lCiAgZ2dwbG90KGFlcyhQQzEsIFBDMikpICsKICBnZW9tX3BvaW50KGFlcyhmaWxsID0gb3JnYW5fbmFtZSksIGNvbG9yID0gImdyYXkyNSIsIGFscGhhID0gMC43LCBzaGFwZT0yMSwgc2l6ZSA9IDIsIHN0cm9rZSA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKyAKICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNhbXBsZV9pZCksIHZqdXN0ID0gMSxoanVzdCA9IDAsIG51ZGdlX3kgPS0xKSArCiAgeGxhYihwYXN0ZSgiUEMxIiwgc2FtcGxlX3BjYUBSMlsxXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlKCJQQzIiLCBzYW1wbGVfcGNhQFIyWzJdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID1lbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KC0wLjAxLCAnY20nKSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAzLCBieXJvdyA9IFRSVUUpKSAKCgojZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9wY2FfMS5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCiMgZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9wY2FfMV93X2ZpbHRlci5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCiMgCiMgcGxvdF9kYXRhICU+JQojICAgZ2dwbG90KGFlcyhQQzEsIFBDMikpICsKIyAgIGdlb21fcG9pbnQoYWVzKGZpbGwgPSBvcmdhbl9uYW1lKSwgY29sb3IgPSAiZ3JheTI1IiwgYWxwaGEgPSAwLjcsIHNoYXBlPTIxLCBzaXplID0gMiwgc3Ryb2tlID0gMC41KSArCiMgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsgCiMgICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNhbXBsZV9pZCksIHZqdXN0ID0gMSxoanVzdCA9IDAsIG51ZGdlX3kgPS0xKSArCiMgICB4bGFiKHBhc3RlKCJQQzEiLCBzYW1wbGVfcGNhQFIyWzFdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwojICAgeWxhYihwYXN0ZSgiUEMyIiwgc2FtcGxlX3BjYUBSMlsyXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKIyAgIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPWVsZW1lbnRfYmxhbmsoKSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiMgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMiwgYnlyb3cgPSBUUlVFKSkgCiMgCiMgI2dnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9zYW1wbGVfcGNhXzIucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQojICMgZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9wY2FfMl93X2ZpbHRlci5wZGYiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcpCgoKYGBgCgojIyNFeHRyYSBQQ0EgcGxvdHMgbm90IGluIHJlcG9ydApgYGB7cn0Kc2FtcGxlX3BjYSA8LQogIHRtbV9zYW1wbGUgJT4lIAogICMgZ2F0aGVyKHNhbXBsZV9uYW1lLCB0bW0sIC0xKSAlPiUgCiAgIyBncm91cF9ieSh0YXJnZXRfaWQpICU+JQogICMgbXV0YXRlKHNkID0gc2QodG1tKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lIAogICMgZmlsdGVyKHNkID4gMCkgJT4lIAogICMgc2VsZWN0KC1zZCkgJT4lIAogICMgc3ByZWFkKHNhbXBsZV9uYW1lLCB0bW0pICU+JSAKICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCl7bG9nMTAoeCsxKX0pICU+JQogIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgc2NhbGUoKSAlPiUKICB0KCkgJT4lCiAgcGNhKG5QY3MgPSA4KQoKI3NhbXBsZV9wY2FAc2NvcmVzCgpzdW1tYXJ5KHNhbXBsZV9wY2EpCgpwbG90X2RhdGEgPC0gc2FtcGxlX3BjYSAlPiUgCiAgc2NvcmVzKCkgJT4lIAogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJzYW1wbGVfaWQiKSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhLAogICAgICAgICAgICBieSA9IGMoInNhbXBsZV9pZCIgPSAiSUQiKSkKCgpyZWdpb25fY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QocmVnaW9uX3Rpc3N1ZV9uYW1lLCByZWdpb25fdGlzc3VlX2NvbG9yLCBvcmdhbl9uYW1lKSAlPiUgdW5pcXVlKCkgJT4lIGZpbHRlcihvcmdhbl9uYW1lID09ICJCcmFpbiIpICU+JSBzZWxlY3QoLW9yZ2FuX25hbWUpCnBhbCA8LSAgcmVnaW9uX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl9jb2xvcnMkcmVnaW9uX3Rpc3N1ZV9uYW1lKQoKcGxvdF9kYXRhICU+JQogIGdncGxvdChhZXMoUEMxLCBQQzIpKSArCiAgZ2VvbV9wb2ludChhZXMoZmlsbCA9IHJlZ2lvbl90aXNzdWVfbmFtZSksICBjb2xvciA9ICJncmF5MjUiLCBhbHBoYSA9IDAuNywgc2hhcGU9MjEsIHNpemUgPSAyLCBzdHJva2UgPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwsIG5hLnZhbHVlID0gIiNGRkZGRkYiKSArIAogIHhsaW0oLTc1LDApICsKICB5bGltKC0xMCw1KSArCiAgeGxhYihwYXN0ZSgiUEMxIiwgc2FtcGxlX3BjYUBSMlsxXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlKCJQQzIiLCBzYW1wbGVfcGNhQFIyWzJdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID1lbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KC0wLjAxLCAnY20nKSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyLCBieXJvdyA9IFRSVUUpKSAKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9icmFpbl93X2FsbF9zYW1wbGVfcGNhLnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKCgpgYGAKCmBgYHtyfQoKYnJhaW5fcGNhIDwtCiAgdG1tX3NhbXBsZSAlPiUgc2VsZWN0KGModGFyZ2V0X2lkLCBtZXRhZGF0YSAlPiUgZmlsdGVyKG9yZ2FuX25hbWUgPT0iQnJhaW4iKSAlPiUgLiRJRCkpJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJ0YXJnZXRfaWQiKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IDgpCgojYnJhaW5fcGNhQHNjb3JlcwoKCnBsb3RfZGF0YSA8LSBicmFpbl9wY2EgJT4lIAogIHNjb3JlcygpICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAic2FtcGxlX2lkIikgJT4lIAogIGxlZnRfam9pbihtZXRhZGF0YSwKICAgICAgICAgICAgYnkgPSBjKCJzYW1wbGVfaWQiID0gIklEIikpCgoKcmVnaW9uX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KHJlZ2lvbl90aXNzdWVfbmFtZSwgcmVnaW9uX3Rpc3N1ZV9jb2xvcikgJT4lIHVuaXF1ZSgpCnBhbCA8LSAgcmVnaW9uX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl9jb2xvcnMkcmVnaW9uX3Rpc3N1ZV9uYW1lKQoKcGxvdF9kYXRhICU+JQogIGdncGxvdChhZXMoUEMxLCBQQzIpKSArCiAgZ2VvbV9wb2ludChhZXMoZmlsbCA9IHJlZ2lvbl90aXNzdWVfbmFtZSksIGNvbG9yID0gImdyYXkyNSIsIGFscGhhID0gMC43LCBzaGFwZT0yMSwgc2l6ZSA9IDIsIHN0cm9rZSA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKyAKICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNhbXBsZV9pZCksIHZqdXN0ID0gMSxoanVzdCA9IDAsIG51ZGdlX3kgPS0xKSArCiAgeGxhYihwYXN0ZSgiUEMxIiwgYnJhaW5fcGNhQFIyWzFdICogMTAwLCAiJSBvZiB0aGUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUoIlBDMiIsIGJyYWluX3BjYUBSMlsyXSAqIDEwMCwgIiUgb2YgdGhlIHZhcmlhbmNlIikpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgtMC4zLCAnY20nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuc3BhY2luZy54ID0gdW5pdCgtMC4wMSwgJ2NtJykpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMiwgYnlyb3cgPSBUUlVFKSkgCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvZGF0YV9wcmVzZW50YXRpb24vYnJhaW5fc2FtcGxlX3BjYS5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCgoKCmBgYAoKIyNGaWd1cmUgMkIgLSBVTUFQIFBsb3QKYGBge3J9CndpZGVfZGF0YSA9IHRtbV9zYW1wbGUgCnNlZWQgPSA0Cm5fZXBvY2hzID0gMTAwMApuX25laWdoYm9ycyA9IDE1CnNldC5zZWVkKHNlZWQpCiAgICAKcGNhX3JlcyA8LQogIHdpZGVfZGF0YSAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpe2xvZzEwKHgrMSl9KSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKGNvbG5hbWVzKHdpZGVfZGF0YSlbMV0pICU+JQogIHNjYWxlKCkgJT4lCiAgdCgpICU+JQogIHBjYShuUGNzID0gZGltKC4pWzFdKQogICAgCiAgICAKcGNfbGltIDwtCiAgd2hpY2gocGNhX3Jlc0BSMmN1bSA+IDAuOClbMV0KCnBjX2xpbV9zZCA8LQogIHJldih3aGljaChwY2FfcmVzQHNEZXYgPiAxKSlbMV0KCnNldC5zZWVkKHNlZWQpCnVtYXBfcmVzIDwtIHBjYV9yZXNAc2NvcmVzWywgMTpwY19saW1dICU+JQogIHVtYXAobl9uZWlnaGJvcnMgPSBuX25laWdoYm9ycywKICAgICAgIG5fZXBvY2hzID0gbl9lcG9jaHMpICU+JQogICAgICAgI21ldHJpYyA9ICJjb3JyZWxhdGlvbiIpICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHNldF9uYW1lcyhwYXN0ZTAoIlVNQVAiLCAxOm5jb2woLikpKSAlPiUKICBtdXRhdGUoc2FtcGxlID0gcm93bmFtZXMocGNhX3Jlc0BzY29yZXMpKSAlPiUKICBzZWxlY3Qoc2FtcGxlLCBldmVyeXRoaW5nKCkpICU+JSAKICBsZWZ0X2pvaW4obWV0YWRhdGEsIGJ5ID0gYygic2FtcGxlIiA9ICJJRCIpKQoKb3JnYW5fY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3Qob3JnYW5fbmFtZSwgb3JnYW5fY29sb3IpICU+JSB1bmlxdWUoKQpwYWwgPC0gIG9yZ2FuX2NvbG9ycyRvcmdhbl9jb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBvcmdhbl9jb2xvcnMkb3JnYW5fbmFtZSkKCiMgdW1hcF9yZXMgJT4lICBnZ3Bsb3QoYWVzKFVNQVAxLCBVTUFQMiwgIGNvbG9yID0gb3JnYW5fbmFtZSkpICsKIyAgICBnZW9tX3BvaW50KGFscGhhID0gMC44KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwpCnVtYXBfcmVzICU+JSAgZ2dwbG90KGFlcyhVTUFQMSwgVU1BUDIpKSArCiAgZ2VvbV9wb2ludCgKICAgIGFlcyhmaWxsID0gb3JnYW5fbmFtZSksCiAgICBjb2xvciA9ICJncmF5MjUiLAogICAgYWxwaGEgPSAwLjcsCiAgICBzaGFwZSA9IDIxLAogICAgc2l6ZSA9IDIsCiAgICBzdHJva2UgPSAwLjUKICApICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSArIAogICMgdGhlbWVfYncoKSArIHRoZW1lKAogICMgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgIyAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgIyAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgIyAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwKICAjICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpCiAgIyApCiAgdGhlbWVfY2xhc3NpYygpICsgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLTAuMywgJ2NtJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIsIGJ5cm93ID0gVFJVRSkpIAoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV91bWFwX2V1Y2xpZGVhbi5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUuNSkKYGBgCgojI0ZpZ3VyZSAyQyAtIEJyYWluIFVtYXAgUGxvdApgYGB7cn0KI1Bsb3QgZm9jdXNpbmcgb25seSBvbiBicmFpbiBzYW1wbGVzLCBidXQgVU1BUCB3YXMgYmFzZWQgb24gdGhlIHdob2xlIHNhbXBsZSBzZXQsIG5vdCBvbmx5IGJyaWFuIHNhbXBsZXMuCgp3aWRlX2RhdGEgPSB0bW1fc2FtcGxlIApzZWVkID0gNApuX2Vwb2NocyA9IDEwMDAKbl9uZWlnaGJvcnMgPSAxNQpzZXQuc2VlZChzZWVkKQogICAgCnBjYV9yZXMgPC0KICB3aWRlX2RhdGEgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcyhjb2xuYW1lcyh3aWRlX2RhdGEpWzFdKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IGRpbSguKVsxXSkKICAgIAogICAgCnBjX2xpbSA8LQogIHdoaWNoKHBjYV9yZXNAUjJjdW0gPiAwLjgpWzFdCgpwY19saW1fc2QgPC0KICByZXYod2hpY2gocGNhX3Jlc0BzRGV2ID4gMSkpWzFdCgpzZXQuc2VlZChzZWVkKQp1bWFwX3JlcyA8LSBwY2FfcmVzQHNjb3Jlc1ssIDE6cGNfbGltXSAlPiUKICB1bWFwKG5fbmVpZ2hib3JzID0gbl9uZWlnaGJvcnMsCiAgICAgICBuX2Vwb2NocyA9IG5fZXBvY2hzKSAlPiUKICAgICAgICNtZXRyaWMgPSAiY29ycmVsYXRpb24iKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBzZXRfbmFtZXMocGFzdGUwKCJVTUFQIiwgMTpuY29sKC4pKSkgJT4lCiAgbXV0YXRlKHNhbXBsZSA9IHJvd25hbWVzKHBjYV9yZXNAc2NvcmVzKSkgJT4lCiAgc2VsZWN0KHNhbXBsZSwgZXZlcnl0aGluZygpKSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhLCBieSA9IGMoInNhbXBsZSIgPSAiSUQiKSkKCgpyZWdpb25fY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QocmVnaW9uX3Rpc3N1ZV9uYW1lLCByZWdpb25fdGlzc3VlX2NvbG9yLCBvcmdhbl9uYW1lKSAlPiUgdW5pcXVlKCkgJT4lIGZpbHRlcihvcmdhbl9uYW1lID09ICJCcmFpbiIpICU+JSBzZWxlY3QoLW9yZ2FuX25hbWUpCnBhbCA8LSAgcmVnaW9uX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl9jb2xvcnMkcmVnaW9uX3Rpc3N1ZV9uYW1lKQojIHVtYXBfcmVzICU+JSAgZ2dwbG90KGFlcyhVTUFQMSwgVU1BUDIsICBjb2xvciA9IG9yZ2FuX25hbWUpKSArCiMgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuOCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsKQp1bWFwX3JlcyAlPiUgIGdncGxvdChhZXMoVU1BUDEsIFVNQVAyKSkgKwogIGdlb21fcG9pbnQoCiAgICBhZXMoZmlsbCA9IHJlZ2lvbl90aXNzdWVfbmFtZSksCiAgICBjb2xvciA9ICJncmF5MjUiLAogICAgYWxwaGEgPSAwLjcsCiAgICBzaGFwZSA9IDIxLAogICAgc2l6ZSA9IDIsCiAgICBzdHJva2UgPSAwLjUKICApICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsLCBuYS52YWx1ZSA9ICIjRkZGRkZGIikgKyAKICAjIHRoZW1lX2J3KCkgKyB0aGVtZSgKICAjICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICMgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICMgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICMgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksCiAgIyAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQogICMgKQogIHhsaW0oLTExLC02KSArCiAgeWxpbSgtOCwtMikgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID1lbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KC0wLjMsICdjbScpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KC0wLjAxLCAnY20nKSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyLCBieXJvdyA9IFRSVUUpKSAKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9zYW1wbGVfZm9jdXNfYnJhaW5fdW1hcF9ldWNsaWRlYW4ucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1LjUpCgpgYGAKIyMjRXh0cmEgVU1BUCBwbG90IG5vdCBpbiByZXBvcnQKYGBge3J9CiNVTUFQIHBsb3QgYmFzZWQgb25seSBvbiBicmFpbiBzYW1wbGVzLCB0aHVzIGRpZmZlcmVudCB0aGFuIHRoZSBwbG90IGFib3ZlLgoKd2lkZV9kYXRhID0gdG1tX3NhbXBsZSAlPiUgc2VsZWN0KGModGFyZ2V0X2lkLCBtZXRhZGF0YSAlPiUgZmlsdGVyKG9yZ2FuX25hbWUgPT0gIkJyYWluIikgJT4lIC4kSUQpKQpzZWVkID0gNApuX2Vwb2NocyA9IDEwMDAKbl9uZWlnaGJvcnMgPSAxNQpzZXQuc2VlZChzZWVkKQogICAgCnBjYV9yZXMgPC0KICB3aWRlX2RhdGEgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcyhjb2xuYW1lcyh3aWRlX2RhdGEpWzFdKSAlPiUKICBzY2FsZSgpICU+JQogIHQoKSAlPiUKICBwY2EoblBjcyA9IGRpbSguKVsxXSkKICAgIAogICAgCnBjX2xpbSA8LQogIHdoaWNoKHBjYV9yZXNAUjJjdW0gPiAwLjgpWzFdCgpwY19saW1fc2QgPC0KICByZXYod2hpY2gocGNhX3Jlc0BzRGV2ID4gMSkpWzFdCgpzZXQuc2VlZChzZWVkKQp1bWFwX3JlcyA8LSBwY2FfcmVzQHNjb3Jlc1ssIDE6cGNfbGltXSAlPiUKICB1bWFwKG5fbmVpZ2hib3JzID0gbl9uZWlnaGJvcnMsCiAgICAgICBuX2Vwb2NocyA9IG5fZXBvY2hzKSAlPiUKICAgICAgICNtZXRyaWMgPSAiY29ycmVsYXRpb24iKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBzZXRfbmFtZXMocGFzdGUwKCJVTUFQIiwgMTpuY29sKC4pKSkgJT4lCiAgbXV0YXRlKHNhbXBsZSA9IHJvd25hbWVzKHBjYV9yZXNAc2NvcmVzKSkgJT4lCiAgc2VsZWN0KHNhbXBsZSwgZXZlcnl0aGluZygpKSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhLCBieSA9IGMoInNhbXBsZSIgPSAiSUQiKSkKCnJlZ2lvbl90aXNzdWVfY29sb3JzIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QocmVnaW9uX3Rpc3N1ZV9uYW1lLCByZWdpb25fdGlzc3VlX2NvbG9yKSAlPiUgdW5pcXVlKCkKcGFsIDwtICByZWdpb25fdGlzc3VlX2NvbG9ycyRyZWdpb25fdGlzc3VlX2NvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHJlZ2lvbl90aXNzdWVfY29sb3JzJHJlZ2lvbl90aXNzdWVfbmFtZSkKCiMgdW1hcF9yZXMgJT4lICBnZ3Bsb3QoYWVzKFVNQVAxLCBVTUFQMiwgIGNvbG9yID0gb3JnYW5fbmFtZSkpICsKIyAgICBnZW9tX3BvaW50KGFscGhhID0gMC44KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwpCnVtYXBfcmVzICU+JSAgZ2dwbG90KGFlcyhVTUFQMSwgVU1BUDIpKSArCiAgZ2VvbV9wb2ludCgKICAgIGFlcyhmaWxsID0gcmVnaW9uX3Rpc3N1ZV9uYW1lKSwKICAgIGNvbG9yID0gImdyYXkyNSIsCiAgICBhbHBoYSA9IDAuNywKICAgIHNoYXBlID0gMjEsCiAgICBzaXplID0gMiwKICAgIHN0cm9rZSA9IDAuNQogICkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsgCiAgIyB0aGVtZV9idygpICsgdGhlbWUoCiAgIyAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICMgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICAjICkKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICMgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLTAuMywgJ2NtJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEsIGJ5cm93ID0gVFJVRSkpIAoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2RhdGFfcHJlc2VudGF0aW9uL3NhbXBsZV9icmFpbl91bWFwX2V1Y2xpZGVhbl9sLnBkZiIsIHdpZHRoID0gNS41LCBoZWlnaHQgPSAzKQpgYGAKIyNGaWd1cmUgMkQgLSAgZ3JvdXBlZCBzYW1wbGUgdG8gc2FtcGxlIGNvcnJlbGF0aW9uIHBsb3RzCmBgYHtyfQppZihmaWxlLmV4aXN0cygiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fc2FtcGxlLmNzdiIpKSB7CiAgc2FtcGxlX3RtbV9zcGVhcm1hbiA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fc2FtcGxlLmNzdiIpCn0gZWxzZSB7CiAgc2FtcGxlX3RtbV9zcGVhcm1hbiA8LSAgdG1tX3NhbXBsZSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgYXNfdGliYmxlKHJvd25hbWVzID0gInNhbXBsZV9uYW1lIikKICB3cml0ZV9jc3YoYXMuZGF0YS5mcmFtZShzYW1wbGVfdG1tX3NwZWFybWFuKSAlPiUgYXNfdGliYmxlKCksIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX3NhbXBsZS5jc3YiKQp9Cgpjb3JyZWxhdGlvbl90b19kaWZmZXJlbnRfb3JnYW5zIDwtIHRpYmJsZShzYW1wbGVfbmFtZSA9IGMoKSwgY29ycmVsYXRpb24gPSBjKCkpCmZvciAoc2FtcGxlIGluIG1ldGFkYXRhJElEKXsKICBzYW1wbGVfb3JnYW4gPC0gbWV0YWRhdGEgJT4lIGZpbHRlcihJRCA9PSBzYW1wbGUpICU+JSAuJG9yZ2FuX25hbWUgJT4lIHVuaXF1ZSgpCiAgZGlmZmVyZW50X29yZ2FuX3NhbXBsZXMgPC0gbWV0YWRhdGEgJT4lIGZpbHRlcihvcmdhbl9uYW1lICE9IHNhbXBsZV9vcmdhbikgJT4lIC4kSUQKICBzYW1wbGVfY29ycmVsYXRpb24gPC0gc2FtcGxlX3RtbV9zcGVhcm1hbiAlPiUgZmlsdGVyKHNhbXBsZV9uYW1lICVpbiUgZGlmZmVyZW50X29yZ2FuX3NhbXBsZXMpICU+JSBzZWxlY3QoInNhbXBsZV9uYW1lIiwgc2FtcGxlKSAlPiUgcmVuYW1lKCJjb3JyZWxhdGlvbiIgPSBzYW1wbGUpCiAgY29ycmVsYXRpb25fdG9fZGlmZmVyZW50X29yZ2FucyA8LSByYmluZChjb3JyZWxhdGlvbl90b19kaWZmZXJlbnRfb3JnYW5zLCBzYW1wbGVfY29ycmVsYXRpb24pIAp9Cgpjb3JyZWxhdGlvbl90b19zYW1lX29yZ2FuIDwtIHRpYmJsZShzYW1wbGVfbmFtZSA9IGMoKSwgY29ycmVsYXRpb24gPSBjKCkpCmZvciAoc2FtcGxlIGluIG1ldGFkYXRhJElEKXsKICBzYW1wbGVfb3JnYW4gPC0gbWV0YWRhdGEgJT4lIGZpbHRlcihJRCA9PSBzYW1wbGUpICU+JSAuJG9yZ2FuX25hbWUgJT4lIHVuaXF1ZSgpCiAgc2FtZV9vcmdhbl9zYW1wbGVzIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIob3JnYW5fbmFtZSA9PSBzYW1wbGVfb3JnYW4pICU+JSBmaWx0ZXIoSUQgIT0gc2FtcGxlKSAlPiUgLiRJRAogIHNhbXBsZV9jb3JyZWxhdGlvbiA8LSBzYW1wbGVfdG1tX3NwZWFybWFuICU+JSBmaWx0ZXIoc2FtcGxlX25hbWUgJWluJSBzYW1lX29yZ2FuX3NhbXBsZXMpICU+JSBzZWxlY3QoInNhbXBsZV9uYW1lIiwgc2FtcGxlKSAlPiUgcmVuYW1lKCJjb3JyZWxhdGlvbiIgPSBzYW1wbGUpCiAgY29ycmVsYXRpb25fdG9fc2FtZV9vcmdhbiA8LSByYmluZChjb3JyZWxhdGlvbl90b19zYW1lX29yZ2FuLCBzYW1wbGVfY29ycmVsYXRpb24pIAp9Cgpjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZSA8LSB0aWJibGUoc2FtcGxlX25hbWUgPSBjKCksIGNvcnJlbGF0aW9uID0gYygpKQpmb3IgKHNhbXBsZSBpbiBtZXRhZGF0YSRJRCl7CiAgc2FtcGxlX3Rpc3N1ZSA8LSBtZXRhZGF0YSAlPiUgZmlsdGVyKElEID09IHNhbXBsZSkgJT4lIC4kdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCiAgc2FtZV90aXNzdWVfc2FtcGxlcyA8LSBtZXRhZGF0YSAlPiUgZmlsdGVyKHRpc3N1ZV9uYW1lID09IHNhbXBsZV90aXNzdWUpICU+JSBmaWx0ZXIoSUQgIT0gc2FtcGxlKSAlPiUgLiRJRAogIHNhbXBsZV9jb3JyZWxhdGlvbiA8LSBzYW1wbGVfdG1tX3NwZWFybWFuICU+JSBmaWx0ZXIoc2FtcGxlX25hbWUgJWluJSBzYW1lX3Rpc3N1ZV9zYW1wbGVzKSAlPiUgc2VsZWN0KCJzYW1wbGVfbmFtZSIsIHNhbXBsZSkgJT4lIHJlbmFtZSgiY29ycmVsYXRpb24iID0gc2FtcGxlKQogIGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlIDwtIHJiaW5kKGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlLCBzYW1wbGVfY29ycmVsYXRpb24pIAp9Cgpjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUgPC0gdGliYmxlKHNhbXBsZV9uYW1lID0gYygpLCBjb3JyZWxhdGlvbiA9IGMoKSwgdGlzc3VlX25hbWUgPSBjKCkpCmZvciAoc2FtcGxlIGluIG1ldGFkYXRhJElEKXsKICBzYW1wbGVfdGlzc3VlIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIoSUQgPT0gc2FtcGxlKSAlPiUgLiR0aXNzdWVfbmFtZSAlPiUgdW5pcXVlKCkKICBzYW1lX3Rpc3N1ZV9zYW1wbGVzIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIodGlzc3VlX25hbWUgPT0gc2FtcGxlX3Rpc3N1ZSkgJT4lIC4kSUQKICBzYW1wbGVfY29ycmVsYXRpb24gPC0gc2FtcGxlX3RtbV9zcGVhcm1hbiAlPiUgZmlsdGVyKHNhbXBsZV9uYW1lICVpbiUgc2FtZV90aXNzdWVfc2FtcGxlcykgJT4lIHNlbGVjdCgic2FtcGxlX25hbWUiLCBzYW1wbGUpICU+JSBmaWx0ZXIoc2FtcGxlX25hbWUgIT0gc2FtcGxlKSAlPiUgIGxlZnRfam9pbihtZXRhZGF0YSAlPiUgc2VsZWN0KElELCB0aXNzdWVfbmFtZSksIGJ5PSBjKCJzYW1wbGVfbmFtZSIgPSAiSUQiKSkgJT4lIHJlbmFtZSgiY29ycmVsYXRpb24iID0gc2FtcGxlKQogIGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlX2J5X3Rpc3N1ZSA8LSByYmluZChjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUsIHNhbXBsZV9jb3JyZWxhdGlvbikgCn0KCmNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlX2J5X3Rpc3N1ZSA8LSBjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUgJT4lIAogIG11dGF0ZSh0aXNzdWVfbmFtZSA9IHN0cl90b19zZW50ZW5jZSh0aXNzdWVfbmFtZSkpICU+JSAKICBncm91cF9ieSh0aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShtaW4gPSBtaW4oY29ycmVsYXRpb24pKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBhcnJhbmdlKG1pbikKCnAxIDwtIGNvcnJlbGF0aW9uX3RvX2RpZmZlcmVudF9vcmdhbnMgJT4lCiAgZ2dwbG90KGFlcyhjb3JyZWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSAgKyB4bGltKDAuNSwxKSsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCJncmF5OTAiKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55LmxlZnQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpICsgCiAgZ2d0aXRsZSgiRGlmZmVyZW50IG9yZ2FucyIpCgpwMiA8LSBjb3JyZWxhdGlvbl90b19zYW1lX29yZ2FuICU+JQogIGdncGxvdChhZXMoY29ycmVsYXRpb24pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCkgKyB4bGltKDAuNSwxKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgiZ3JheTkwIiksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IDAsIGFkZCA9IDApKSArCiAgZ2d0aXRsZSgiU2FtZSBvcmdhbnMiKQoKcDMgPC0gY29ycmVsYXRpb25fdG9fc2FtZV90aXNzdWUgJT4lCiAgZ2dwbG90KGFlcyhjb3JyZWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArIHhsaW0oMC41LDEpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoImdyYXk5MCIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkKICAgICAgICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLCBhZGQgPSAwKSkgKwogIGdndGl0bGUoIlNhbWUgdGlzc3VlIikKCnA0IDwtIGNvcnJlbGF0aW9uX3RvX3NhbWVfdGlzc3VlX2J5X3Rpc3N1ZSAlPiUgCiAgbXV0YXRlKHRpc3N1ZV9uYW1lID0gZmFjdG9yKHRpc3N1ZV9uYW1lLCBjb3JyZWxhdGlvbl90b19zYW1lX3Rpc3N1ZV9ieV90aXNzdWUkdGlzc3VlX25hbWUgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZSgpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGNvcnJlbGF0aW9uLCB5ID0gdGlzc3VlX25hbWUpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2l6ZT0wLjMpICsgeGxpbSgwLjUsMSkgKwogIHRoZW1lX2J3KCkgKyAKICB4bGFiICgiU3BlYXJtYW4ncyByb2giKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKQoKcDEgLyBwMiAvIHAzIC8gcDQgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxLCAxLCAxNSkpCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvZGF0YV9wcmVzZW50YXRpb24vc3BlYXJtYW5fY29ycl9zYW1wbGVfdnNfdGlzc3VlX29yZ2FuLnBkZiIsIGhlaWdodCA9IDE0LCB3aWR0aCA9IDQpCmBgYAoKI1NwZWNpZmljaXR5IGFuZCBkaXN0cmlidXRpb24gYW5ub3RhdGlvbgojI0Zvcm11bGFzCmBgYHtyfQojY2FsY3VsYXRlX3RhdV9zY29yZSBhbmQgaHBhX2dlbmVfY2xhc3NpZmljYXRpb24gdGFrZW4gZnJvbSBNYXggS2FybHNzb24KY2FsY3VsYXRlX3RhdV9zY29yZSA8LSAKICBmdW5jdGlvbih3aWRlX2RhdGEpIHsKICAgIG1heF9leHAgPC0gCiAgICAgIGFwcGx5KHdpZGVfZGF0YSwKICAgICAgICAgICAgTUFSR0lOID0gMSwKICAgICAgICAgICAgZnVuY3Rpb24oeCkgbWF4KHgsIG5hLnJtID0gVCkpCiAgICAKICAgIE4gPC0gCiAgICAgIGFwcGx5KHdpZGVfZGF0YSwKICAgICAgICAgICAgTUFSR0lOID0gMSwKICAgICAgICAgICAgZnVuY3Rpb24oeCkgbGVuZ3RoKHdoaWNoKCFpcy5uYSh4KSkpKQogICAgCiAgICBleHByZXNzaW9uX3N1bSA8LSAKICAgICAgd2lkZV9kYXRhICU+JSAKICAgICAgc3dlZXAoTUFSR0lOID0gMSwgCiAgICAgICAgICAgIFNUQVRTID0gbWF4X2V4cCwgCiAgICAgICAgICAgIEZVTiA9IGAvYCkgJT4lIAogICAgICB7MSAtIC59ICU+JSAKICAgICAgYXBwbHkoTUFSR0lOID0gMSwKICAgICAgICAgICAgZnVuY3Rpb24oeCkgc3VtKHgsIG5hLnJtID0gVCkpCiAgICAKICAgIAogICAgdGF1X3Njb3JlIDwtIAogICAgICAoZXhwcmVzc2lvbl9zdW0gLyAoTiAtIDEpKSAlPiUgCiAgICAgIGVuZnJhbWUoImdlbmUiLCAidGF1X3Njb3JlIikKICAgIAogICAgdGF1X3Njb3JlCiAgfQoKaHBhX2dlbmVfY2xhc3NpZmljYXRpb24gPC0gCiAgI2ZlZWQgaW4gbG9uZyBkYXRhCiAgZnVuY3Rpb24oZGF0YSwgZXhwcmVzc2lvbl9jb2wsIHRpc3N1ZV9jb2wsIGdlbmVfY29sLCBlbnJfZm9sZCwgbWF4X2dyb3VwX24sIGRldF9saW0gPSAxKSB7CiAgICBkYXRhXyA8LSAKICAgICAgZGF0YSAlPiUgCiAgICAgIHNlbGVjdChnZW5lID0gZ2VuZV9jb2wsCiAgICAgICAgICAgICBleHByZXNzaW9uID0gZXhwcmVzc2lvbl9jb2wsCiAgICAgICAgICAgICB0aXNzdWUgPSB0aXNzdWVfY29sKSAlPiUgCiAgICAgIG11dGF0ZShleHByZXNzaW9uID0gcm91bmQoZXhwcmVzc2lvbiwgNCkpIAogICAgCiAgICBpZihhbnkoaXMubmEoZGF0YV8kZXhwcmVzc2lvbikpKSBzdG9wKCJOQXMgaW4gZXhwcmVzc2lvbiBjb2x1bW4iKQogICAgaWYoYW55KGlzLm5hKGRhdGFfJGdlbmUpKSkgc3RvcCgiTkFzIGluIGdlbmUgY29sdW1uIikKICAgIGlmKGFueShpcy5uYShkYXRhXyR0aXNzdWUpKSkgc3RvcCgiTkFzIGluIHRpc3N1ZSBjb2x1bW4iKQogICAgCiAgICBuX2dyb3VwcyA8LSBsZW5ndGgodW5pcXVlKGRhdGFfJHRpc3N1ZSkpCiAgICAKICAgIGdlbmVfY2xhc3NfaW5mbyA8LSAKICAgICAgZGF0YV8gJT4lCiAgICAgIGdyb3VwX2J5KGdlbmUpICU+JQogICAgICBzdW1tYXJpc2UoCiAgICAgICAgCiAgICAgICAgIyBHZW5lIGV4cHJlc3Npb24gZGlzdHJpYnV0aW9uIG1ldHJpY3MKICAgICAgICBtZWFuX2V4cCA9IG1lYW4oZXhwcmVzc2lvbiwgbmEucm0gPSBUKSwKICAgICAgICBtaW5fZXhwID0gbWluKGV4cHJlc3Npb24sIG5hLnJtID0gVCksCiAgICAgICAgbWF4X2V4cCA9IG1heChleHByZXNzaW9uLCBuYS5ybSA9IFQpLCAKICAgICAgICBtYXhfMm5kID0gc29ydChleHByZXNzaW9uKVtsZW5ndGgoZXhwcmVzc2lvbiktMV0sCiAgICAgICAgCiAgICAgICAgIyBFeHByZXNzaW9uIGZyZXF1ZW5jeSBtZXRyaWNzCiAgICAgICAgbl9leHAgPSBsZW5ndGgod2hpY2goZXhwcmVzc2lvbiA+PSBkZXRfbGltKSksCiAgICAgICAgZnJhY19leHAgPSBuX2V4cC9sZW5ndGgoZXhwcmVzc2lvblshaXMubmEoZXhwcmVzc2lvbildKSoxMDAsCiAgICAgICAgCiAgICAgICAgIyBMaW1pdCBvZiBlbmhhbmNlbWVudCBtZXRyaWNzCiAgICAgICAgbGltID0gbWF4X2V4cC9lbnJfZm9sZCwgCiAgICAgICAgCiAgICAgICAgZXhwc19vdmVyX2xpbSA9IGxpc3QoZXhwcmVzc2lvblt3aGljaChleHByZXNzaW9uID49IGxpbSAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSldKSwKICAgICAgICBuX292ZXIgPSBsZW5ndGgoZXhwc19vdmVyX2xpbVtbMV1dKSwgCiAgICAgICAgbWVhbl9vdmVyID0gbWVhbihleHBzX292ZXJfbGltW1sxXV0pLAogICAgICAgIG1pbl9vdmVyID0gaWZlbHNlKG5fb3ZlciA9PSAwLCBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4oZXhwc19vdmVyX2xpbVtbMV1dKSksCiAgICAgICAgCiAgICAgICAgbWF4X3VuZGVyX2xpbSA9IG1heChleHByZXNzaW9uW3doaWNoKGV4cHJlc3Npb24gPCBtaW5fb3ZlcildLCBkZXRfbGltKjAuMSksCiAgICAgICAgCiAgICAgICAgCiAgICAgICAgZXhwc19lbmhhbmNlZCA9IGxpc3Qod2hpY2goZXhwcmVzc2lvbi9tZWFuX2V4cCA+PSBlbnJfZm9sZCAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSkpLAogICAgICAgIAogICAgICAgIAogICAgICAgIAogICAgICAgIAogICAgICAgICMgRXhwcmVzc2lvbiBwYXR0ZXJucwogICAgICAgIGVucmljaG1lbnRfZ3JvdXAgPSBwYXN0ZShzb3J0KHRpc3N1ZVt3aGljaChleHByZXNzaW9uID49IGxpbSAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSldKSwgY29sbGFwc2U9IjsiKSwKICAgICAgICAKICAgICAgICBuX2VucmljaGVkID0gbGVuZ3RoKHRpc3N1ZVt3aGljaChleHByZXNzaW9uID49IGxpbSAmIGV4cHJlc3Npb24gPj0gZGV0X2xpbSldKSwKICAgICAgICBuX2VuaGFuY2VkID0gbGVuZ3RoKGV4cHNfZW5oYW5jZWRbWzFdXSksIAogICAgICAgIGVuaGFuY2VkX2luID0gcGFzdGUoc29ydCh0aXNzdWVbZXhwc19lbmhhbmNlZFtbMV1dXSksIGNvbGxhcHNlPSI7IiksCiAgICAgICAgbl9uYSA9IG5fZ3JvdXBzIC0gbGVuZ3RoKGV4cHJlc3Npb24pLAogICAgICAgIG1heF8ybmRfb3JfbGltID0gbWF4KG1heF8ybmQsIGRldF9saW0qMC4xKSwKICAgICAgICB0aXNzdWVzX25vdF9kZXRlY3RlZCA9IHBhc3RlKHNvcnQodGlzc3VlW3doaWNoKGV4cHJlc3Npb24gPCBkZXRfbGltKV0pLCBjb2xsYXBzZT0iOyIpLAogICAgICAgIHRpc3N1ZXNfZGV0ZWN0ZWQgPSBwYXN0ZShzb3J0KHRpc3N1ZVt3aGljaChleHByZXNzaW9uID49IGRldF9saW0pXSksIGNvbGxhcHNlPSI7IikpIAogICAgCiAgICAKICAgIGdlbmVfY2F0ZWdvcmllcyA8LSAKICAgICAgZ2VuZV9jbGFzc19pbmZvICU+JQogICAgICAKICAgICAgbXV0YXRlKAogICAgICAgIHNwZWNfY2F0ZWdvcnkgPSBjYXNlX3doZW4obl9leHAgPT0gMCB+ICJub3QgZGV0ZWN0ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBHZW5lcyB3aXRoIGV4cHJlc3Npb24gZm9sZCB0aW1lcyBtb3JlIHRoYW4gYW55dGhpbmcgZWxzZSBhcmUgdGlzc3VlIGVucmljaGVkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfZXhwL21heF8ybmRfb3JfbGltID49IGVucl9mb2xkIH4gInRpc3N1ZSBlbnJpY2hlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdlbmVzIHdpdGggZXhwcmVzc2lvbiBmb2xkIHRpbWVzIG1vcmUgdGhhbiBvdGhlciB0aXNzdWVzIGluIGdyb3VwcyBvZiBtYXggZ3JvdXBfbiAtIDEgYXJlIGdyb3VwIGVucmljaGVkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfZXhwID49IGxpbSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fb3ZlciA8PSBtYXhfZ3JvdXBfbiAmIG5fb3ZlciA+IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX292ZXIvbWF4X3VuZGVyX2xpbSA+PSBlbnJfZm9sZCB+ICJncm91cCBlbnJpY2hlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdlbmVzIHdpdGggZXhwcmVzc2lvbiBpbiB0aXNzdWVzIGZvbGQgdGltZXMgbW9yZSB0aGFuIHRoZSBtZWFuIGFyZSB0aXNzdWUgZW5oYW5jZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9lbmhhbmNlZCA+IDAgfiAidGlzc3VlIGVuaGFuY2VkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgR2VuZXMgZXhwcmVzc2VkIHdpdGggbG93IHRpc3N1ZSBzcGVjaWZpY2l0eQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVCB+ICJsb3cgdGlzc3VlIHNwZWNpZmljaXR5IiksIAogICAgICAgIAogICAgICAgIAogICAgICAgIGRpc3RfY2F0ZWdvcnkgPSBjYXNlX3doZW4oZnJhY19leHAgPT0gMTAwIH4gImRldGVjdGVkIGluIGFsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcmFjX2V4cCA+PSAzMSB+ICJkZXRlY3RlZCBpbiBtYW55IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fZXhwID4gMSB+ICJkZXRlY3RlZCBpbiBzb21lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fZXhwID09IDEgfiAiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fZXhwID09IDAgfiAibm90IGRldGVjdGVkIiksCiAgICAgICAgCiAgICAgICAgc3BlY19zY29yZSA9IGNhc2Vfd2hlbihzcGVjX2NhdGVnb3J5ID09ICJ0aXNzdWUgZW5yaWNoZWQiIH4gbWF4X2V4cC9tYXhfMm5kX29yX2xpbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gImdyb3VwIGVucmljaGVkIiB+IG1lYW5fb3Zlci9tYXhfdW5kZXJfbGltLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gInRpc3N1ZSBlbmhhbmNlZCIgfiBtYXhfZXhwL21lYW5fZXhwKSkgCiAgICAKICAgIAogICAgCiAgICAKICAgICMjIyMjIFJlbmFtZSBhbmQgZm9ybWF0CiAgICBnZW5lX2NhdGVnb3JpZXMgJT4lCiAgICAgIG11dGF0ZShlbnJpY2hlZF90aXNzdWVzID0gY2FzZV93aGVuKHNwZWNfY2F0ZWdvcnkgJWluJSBjKCJ0aXNzdWUgZW5yaWNoZWQiLCAiZ3JvdXAgZW5yaWNoZWQiKSB+IGVucmljaG1lbnRfZ3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gInRpc3N1ZSBlbmhhbmNlZCIgfiBlbmhhbmNlZF9pbiksCiAgICAgICAgICAgICBuX2VucmljaGVkID0gY2FzZV93aGVuKHNwZWNfY2F0ZWdvcnkgJWluJSBjKCJ0aXNzdWUgZW5yaWNoZWQiLCAiZ3JvdXAgZW5yaWNoZWQiKSB+IG5fZW5yaWNoZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPT0gInRpc3N1ZSBlbmhhbmNlZCIgfiBuX2VuaGFuY2VkKSkgJT4lCiAgICAgIHNlbGVjdChnZW5lLCAKICAgICAgICAgICAgIHNwZWNfY2F0ZWdvcnksIAogICAgICAgICAgICAgZGlzdF9jYXRlZ29yeSwgCiAgICAgICAgICAgICBzcGVjX3Njb3JlLAogICAgICAgICAgICAgbl9leHByZXNzZWQgPSBuX2V4cCwgCiAgICAgICAgICAgICBmcmFjdGlvbl9leHByZXNzZWQgPSBmcmFjX2V4cCwKICAgICAgICAgICAgIG1heF9leHAgPSBtYXhfZXhwLAogICAgICAgICAgICAgZW5yaWNoZWRfdGlzc3VlcywKICAgICAgICAgICAgIG5fZW5yaWNoZWQsCiAgICAgICAgICAgICBuX25hID0gbl9uYSwKICAgICAgICAgICAgIHRpc3N1ZXNfbm90X2RldGVjdGVkLAogICAgICAgICAgICAgdGlzc3Vlc19kZXRlY3RlZCkgCiAgICAKICB9CQoKYGBgCgojI0Fubm90YXRpb24KYGBge3J9CgojIFNwZWNpZmljaXR5IGNsYXNzaWZpY2F0aW9uIGF0IGNvbnNlbnN1cyBsZXZlbApjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgPC0gaHBhX2dlbmVfY2xhc3NpZmljYXRpb24oZGF0YSA9IHRtbV9jb25zZW5zdXNfdGlzc3VlLCBleHByZXNzaW9uX2NvbCA9ICJ0bW0iLCB0aXNzdWVfY29sID0gImNvbnNlbnN1c190aXNzdWVfbmFtZSIsIGdlbmVfY29sID0gInRhcmdldF9pZCIsIGVucl9mb2xkID0gNCwgbWF4X2dyb3VwX24gPSA1LCBkZXRfbGltID0gMSkKCmNvbnNlbnN1c190YXUgPC0gY2FsY3VsYXRlX3RhdV9zY29yZSh0bW1fY29uc2Vuc3VzX3Rpc3N1ZSAgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ByZWFkKGNvbnNlbnN1c190aXNzdWVfbmFtZSwgdG1tKSU+JSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KXtsb2cxMCh4KzEpfSklPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikpIAojY2F0ZWdvcnkgbm90IGRldGVjdGVkIGhhcyBhIHZlcnkgbm9pc3kgdGF1LCBzbyBubyB0YXUgc2NvcmUgZm9yIHRob3NlCmNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyA8LSBjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lIAogIGxlZnRfam9pbihjb25zZW5zdXNfdGF1LCBieSA9IGMoImdlbmUiID0gImdlbmUiKSkgJT4lIAogIG11dGF0ZSh0YXVfc2NvcmUgPSBpZmVsc2Uoc3BlY19jYXRlZ29yeSA9PSAibm90IGRldGVjdGVkIiwgTkEsIHRhdV9zY29yZSkpCgojYXQgcmVnaW9uIGxldmVsCmNsYXNzaWZpY2F0aW9uX3JlZ2lvbiA8LSBocGFfZ2VuZV9jbGFzc2lmaWNhdGlvbihkYXRhID0gdG1tX3JlZ2lvbl90aXNzdWUsIGV4cHJlc3Npb25fY29sID0gInRtbSIsIHRpc3N1ZV9jb2wgPSAicmVnaW9uX3Rpc3N1ZV9uYW1lIiwgZ2VuZV9jb2wgPSAidGFyZ2V0X2lkIiwgZW5yX2ZvbGQgPSA0LCBtYXhfZ3JvdXBfbiA9IDUsIGRldF9saW0gPSAxKQoKcmVnaW9uX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKHRtbV9yZWdpb25fdGlzc3VlICAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwcmVhZChyZWdpb25fdGlzc3VlX25hbWUsIHRtbSkgJT4lICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpe2xvZzEwKHgrMSl9KSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgKQoKY2xhc3NpZmljYXRpb25fcmVnaW9uIDwtIGNsYXNzaWZpY2F0aW9uX3JlZ2lvbiAlPiUgCiAgbGVmdF9qb2luKHJlZ2lvbl90YXUsIGJ5ID0gYygiZ2VuZSIgPSAiZ2VuZSIpKSAlPiUgCiAgbXV0YXRlKHRhdV9zY29yZSA9IGlmZWxzZShzcGVjX2NhdGVnb3J5ID09ICJub3QgZGV0ZWN0ZWQiLCBOQSwgdGF1X3Njb3JlKSkKCiNhdCB0aXNzdWUgbGV2ZWwKY2xhc3NpZmljYXRpb25fdGlzc3VlIDwtIGhwYV9nZW5lX2NsYXNzaWZpY2F0aW9uKGRhdGEgPSB0bW1fdGlzc3VlLCBleHByZXNzaW9uX2NvbCA9ICJ0bW0iLCB0aXNzdWVfY29sID0gInRpc3N1ZV9uYW1lIiwgZ2VuZV9jb2wgPSAidGFyZ2V0X2lkIiwgZW5yX2ZvbGQgPSA0LCBtYXhfZ3JvdXBfbiA9IDUsIGRldF9saW0gPSAxKQoKdGlzc3VlX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKHRtbV90aXNzdWUgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJlYWQodGlzc3VlX25hbWUsIHRtbSkgJT4lICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpe2xvZzEwKHgrMSl9KSU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoInRhcmdldF9pZCIpICkKCmNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSA8LSBjbGFzc2lmaWNhdGlvbl90aXNzdWUgJT4lIAogIGxlZnRfam9pbih0aXNzdWVfdGF1LCBieSA9IGMoImdlbmUiID0gImdlbmUiKSkgJT4lIAogIG11dGF0ZSh0YXVfc2NvcmUgPSBpZmVsc2Uoc3BlY19jYXRlZ29yeSA9PSAibm90IGRldGVjdGVkIiwgTkEsIHRhdV9zY29yZSkpCgpgYGAKCiNGaWd1cmUgMwojI0ZpZ3VyZSAzQSAtIFBpZSBjaGFydHMgYXQgdGlzc3VlIHR5cGUgbGV2ZWwKYGBge3J9CmdlbmVfY2F0ZWdvcnlfcGFsIDwtIGMoIkRldGVjdGVkIGluIGFsbCIgPSAiIzI1MzQ5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIG1hbnkiID0gIiMyYzdmYjgiLAogICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBzb21lIiA9ICIjNDFiNmM0IiwKICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gc2luZ2xlIiA9ICIjYTFkYWI0IiwKICAgICAgICAgICAgICAgICAgICAgICAiTm90IGRldGVjdGVkICIgPSAiCSNiZWJlYmUiLAogICAgICAgICAgICAgICAgICAgICAgICJMb3cgdGlzc3VlIHNwZWNpZmljaXR5IiA9ICJncmV5NDAiLAogICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5oYW5jZWQiID0gIiM5ODRlYTMiLAogICAgICAgICAgICAgICAgICAgICAgICJHcm91cCBlbnJpY2hlZCIgPSAiI0ZGOUQwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbnJpY2hlZCIgPSAiI2U0MWExYyIpCgpwbG90X2RhdGEgPC0KICBjbGFzc2lmaWNhdGlvbl90aXNzdWUgJT4lIAogIHJlbmFtZShzcGVjaWZpY2l0eSA9IHNwZWNfY2F0ZWdvcnksIGRpc3RyaWJ1dGlvbiA9IGRpc3RfY2F0ZWdvcnkpICU+JSAKICBzZWxlY3QoZ2VuZSwgc3BlY2lmaWNpdHksIGRpc3RyaWJ1dGlvbikgJT4lIAogIGdhdGhlcihjbGFzc190eXBlLCBjbGFzcywgLTEpICU+JSAKICBncm91cF9ieShjbGFzc190eXBlLCBjbGFzcykgJT4lIAogIGNvdW50KCkgJT4lCiAgZ3JvdXBfYnkoY2xhc3NfdHlwZSkgJT4lIAogIG11dGF0ZShwZXJjID0gMTAwICogbiAvIHN1bShuKSwKICAgICAgICAgbGFiZWwgPSBwYXN0ZTAobiwgIlxuIiwgcm91bmQocGVyYywgMSksICIlIikpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShjbGFzcyA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoY2xhc3MpLCBzdHJfdG9fc2VudGVuY2UoYygidGlzc3VlIGVucmljaGVkIiwgImdyb3VwIGVucmljaGVkIiwgICJ0aXNzdWUgZW5oYW5jZWQiLCAibG93IHRpc3N1ZSBzcGVjaWZpY2l0eSIsICJkZXRlY3RlZCBpbiBhbGwiLCAiZGV0ZWN0ZWQgaW4gbWFueSIsICJkZXRlY3RlZCBpbiBzb21lIiwgImRldGVjdGVkIGluIHNpbmdsZSIsICJub3QgZGV0ZWN0ZWQiKSkpLAogICAgICAgICBjbGFzc190eXBlID0gZmFjdG9yKHN0cl90b19zZW50ZW5jZShjbGFzc190eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJTcGVjaWZpY2l0eSIsICJEaXN0cmlidXRpb24iKSkpCiAgICAKcGxvdF9kb2RnZSA9IDAuMQpwbG90X2RhdGEgJT4lIAogIGFycmFuZ2UobWF0Y2goY2xhc3MsIHJldihsZXZlbHMoY2xhc3MpKSkpICU+JSAKICBncm91cF9ieShjbGFzc190eXBlKSAlPiUgCiAgbXV0YXRlKHlfc3RhY2sgPSBjdW1zdW0obikgLSBuLzIpICU+JSAKICB7Z2dwbG90KC4sIGFlcygxLCBuLCBmaWxsID0gY2xhc3MsIGdyb3VwID0gY2xhc3MsIAogICAgICAgICAgICAgICAgIGxhYmVsID0gbGFiZWwpKSArCiAgICAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRiwgCiAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgCiAgICAgICAgICAgICAgIHdpZHRoID0gMSkgKyAKICAgICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMS41ICsgcGxvdF9kb2RnZSwgeGVuZCA9IDEuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IHlfc3RhY2ssIHllbmQgPSB5X3N0YWNrKSwgc2l6ZSA9IDAuNSkgKwogICAgICBnZW9tX3RleHRfcmVwZWwoYWVzKHggPSAxLjUgKyBwbG90X2RvZGdlLCB5ID0geV9zdGFjayksIAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBudWRnZV94ID0gcGxvdF9kb2RnZSwgCiAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50LnNpemUgPSAwLjUsIHNpemUgPSAyNC8xMSkgKwogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnZW5lX2NhdGVnb3J5X3BhbCkgKyAKICAgICAgZmFjZXRfd3JhcCh+Y2xhc3NfdHlwZSkgKwogICAgICBjb29yZF9wb2xhcigieSIsc3RhcnQgPSAwKSArCiAgICAgIHRoZW1lX3ZvaWQoKSArIAogICAgICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKGMoMCwwLjgpKSl9CgoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL2NsYXNzX3BpZXNfdGlzc3VlX3R5cGUucGRmIix3aWR0aCA9IDYsIGhlaWdodCA9IDUpCgpgYGAKIyNGaWd1cmUgM0IgLSBEaXN0aWJ1dGlvbiBmb3IgZWFjaCB0aXNzdWUgdHlwZQpgYGB7cn0KCmNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSAlPiUgZ3JvdXBfYnkoZGlzdF9jYXRlZ29yeSkgJT4lIGNvdW50KCkKCm9yZGVyZWRfbmFtZXNfZGlzdHIgPC0gY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAKICBmaWx0ZXIoZGlzdF9jYXRlZ29yeSAlaW4lIGMoImRldGVjdGVkIGluIHNpbmdsZSIsICJkZXRlY3RlZCBpbiBzb21lIiwgImRldGVjdGVkIGluIG1hbnkiLCAiZGV0ZWN0ZWQgaW4gYWxsIikpICU+JSAKICBzZXBhcmF0ZV9yb3dzKHRpc3N1ZXNfZGV0ZWN0ZWQsIHNlcCA9ICI7IikgJT4lIAogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JSAKICBjb3VudCgpICU+JSAKICBncm91cF9ieSh0aXNzdWVzX2RldGVjdGVkKSAlPiUgCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShuKSkgJT4lIAogIGFycmFuZ2Uoc3VtKSAlPiUgCiAgLiR0aXNzdWVzX2RldGVjdGVkICU+JSAKICBzdHJfdG9fc2VudGVuY2UoKQoKZGV0ZWN0aW9uX3BhbGV0dGUgPC0gYygiRGV0ZWN0ZWQgaW4gYWxsIiA9ICIjMjUzNDk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIG1hbnkiID0gIiMyYzdmYjgiLAogICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gc29tZSIgPSAiIzQxYjZjNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBzaW5nbGUiID0gIiNhMWRhYjQiLAogICAgICAgICAgICAgICAgICAgICAgICAiTm90IGRldGVjdGVkICIgPSAiZ3JleSIpCgoKY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JQogIGZpbHRlcigKICAgIGRpc3RfY2F0ZWdvcnkgJWluJSBjKAogICAgICAiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwKICAgICAgImRldGVjdGVkIGluIHNvbWUiLAogICAgICAiZGV0ZWN0ZWQgaW4gbWFueSIsCiAgICAgICJkZXRlY3RlZCBpbiBhbGwiCiAgICApCiAgKSAlPiUKICBzZXBhcmF0ZV9yb3dzKHRpc3N1ZXNfZGV0ZWN0ZWQsIHNlcCA9ICI7IikgJT4lCiAgZ3JvdXBfYnkoZGlzdF9jYXRlZ29yeSwgdGlzc3Vlc19kZXRlY3RlZCkgJT4lCiAgY291bnQoKSAlPiUKICBtdXRhdGUodGlzc3Vlc19kZXRlY3RlZCA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoIHRpc3N1ZXNfZGV0ZWN0ZWQpLCBvcmRlcmVkX25hbWVzX2Rpc3RyKSkgJT4lCiAgbXV0YXRlKGRpc3RfY2F0ZWdvcnkgPSBmYWN0b3IoCiAgICBzdHJfdG9fc2VudGVuY2UoIGRpc3RfY2F0ZWdvcnkpLAogICAgYygKICAgICAgIkRldGVjdGVkIGluIHNpbmdsZSIsCiAgICAgICJEZXRlY3RlZCBpbiBzb21lIiwKICAgICAgIkRldGVjdGVkIGluIG1hbnkiLAogICAgICAiRGV0ZWN0ZWQgaW4gYWxsIgogICAgKQogICkpICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSB0aXNzdWVzX2RldGVjdGVkLCBmaWxsID0gZGlzdF9jYXRlZ29yeSkpICsKICBnZW9tX2NvbCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBkZXRlY3Rpb25fcGFsZXR0ZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IDAsIGFkZCA9IDApKSArCiAgZ2d0aXRsZSgiTnVtYmVyIG9mIGRldGVjdGVkIGdlbmVzIHBlciB0aXNzdWUgdHlwZSIpICArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobnJvdyA9IDIsIGJ5cm93ID0gVFJVRSkpCgogZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL3Rpc3N1ZV9kZXRlY3Rpb25fYS5wZGYiLCBoZWlnaHQgPSAxMS41LCB3aWR0aCA9IDQuNSkKCgpgYGAKCiMjRmlndXJlIDNDIC0gRGlzdHJpYnV0aW9uIGFuZCBUYXUgc2NvcmUKYGBge3J9CmRldGVjdGlvbl9wYWxldHRlIDwtIGMoIkRldGVjdGVkIGluIGFsbCIgPSAiIzI1MzQ5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBtYW55IiA9ICIjMmM3ZmI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIHNvbWUiID0gIiM0MWI2YzQiLAogICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gc2luZ2xlIiA9ICIjYTFkYWI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIk5vdCBkZXRlY3RlZCAiID0gImdyZXkiKQoKcDEgPC0gY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAKICBtdXRhdGUoZGlzdF9jYXRlZ29yeSA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoIGRpc3RfY2F0ZWdvcnkpLCBsZXZlbHMgPSByZXYobmFtZXMoZGV0ZWN0aW9uX3BhbGV0dGUpKSksCiAgICAgICAgIGVucmljaGVkX3Rpc3N1ZXMgPSBzdHJfdG9fc2VudGVuY2UoZW5yaWNoZWRfdGlzc3VlcykpICU+JSAKICBmaWx0ZXIoZGlzdF9jYXRlZ29yeSAhPSAiTm90IGRldGVjdGVkIikgJT4lIAogIGdncGxvdChhZXMoeCA9IHRhdV9zY29yZSwgeSA9IGRpc3RfY2F0ZWdvcnksIGZpbGwgPSBkaXN0X2NhdGVnb3J5KSkgKwogIGdlb21fdmlvbGluKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdlbmVfY2F0ZWdvcnlfcGFsLCBuYW1lID0gIlNwZWNpZmljaXR5IikgKwogIHhsYWIoIlRhdSBzY29yZSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKQogICAgKSAKCiNnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY2xhc3NpZmljYXRpb24vdGF1X3RvX2Rpc3RyaWJ1dGlvbi5wZGYiLHdpZHRoID0gNS41LCBoZWlnaHQgPSA0KQoKCnAyIDwtIGNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSAlPiUKICBnZ3Bsb3QoYWVzKHRhdV9zY29yZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB5bGFiKCJDb3VudCIpKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoImdyYXk5MCIpLAogICAgICAgICNheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpCgpwMi8gcDEgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAzKSkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9kaXN0cmlidXRpb25fYW5kX2RlbmRyb2dyYW1fdGlzc3VlX3dfb3ZlcmFsbC5wZGYiLHdpZHRoID0gNi41LCBoZWlnaHQgPSA2KQpgYGAKIyMjRXh0cmEgRmlndXJlcyBub3QgaW4gcmVwb3J0CmBgYHtyfQpkZXRlY3Rpb25fcGFsZXR0ZSA8LSBjKCJkZXRlY3RlZCBpbiBhbGwiID0gIiMyNTM0OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAiZGV0ZWN0ZWQgaW4gbWFueSIgPSAiIzJjN2ZiOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJkZXRlY3RlZCBpbiBzb21lIiA9ICIjNDFiNmM0IiwKICAgICAgICAgICAgICAgICAgICAgICAgImRldGVjdGVkIGluIHNpbmdsZSIgPSAiI2ExZGFiNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJub3QgZGV0ZWN0ZWQgIiA9ICJncmV5IikKCm9yZGVyZWRfbmFtZXNfZGlzdHIgPC0gY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAgCiAgZmlsdGVyKGRpc3RfY2F0ZWdvcnkgJWluJSBjKCJkZXRlY3RlZCBpbiBzaW5nbGUiIywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICJkZXRlY3RlZCBpbiBzb21lIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSAlPiUgCiAgc2VwYXJhdGVfcm93cyh0aXNzdWVzX2RldGVjdGVkLCBzZXAgPSAiOyIpICU+JSAKICBncm91cF9ieShkaXN0X2NhdGVnb3J5LCB0aXNzdWVzX2RldGVjdGVkKSAlPiUgCiAgY291bnQoKSAlPiUgCiAgZ3JvdXBfYnkodGlzc3Vlc19kZXRlY3RlZCkgJT4lIAogIHN1bW1hcmlzZShzdW0gPSBzdW0obikpICU+JSAKICBhcnJhbmdlKHN1bSkgJT4lIAogIC4kdGlzc3Vlc19kZXRlY3RlZAoKY2xhc3NpZmljYXRpb25fdGlzc3VlICU+JSAgCiAgZmlsdGVyKAogICAgZGlzdF9jYXRlZ29yeSAlaW4lIGMoCiAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiIywKIwogICAgKQogICkgJT4lCiAgc2VwYXJhdGVfcm93cyh0aXNzdWVzX2RldGVjdGVkLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JQogIGNvdW50KCkgJT4lCiAgbXV0YXRlKHRpc3N1ZXNfZGV0ZWN0ZWQgPSBmYWN0b3IodGlzc3Vlc19kZXRlY3RlZCwgb3JkZXJlZF9uYW1lc19kaXN0cikpICU+JQogIG11dGF0ZShkaXN0X2NhdGVnb3J5ID0gZmFjdG9yKAogICAgZGlzdF9jYXRlZ29yeSwKICAgIGMoCiAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiLAogICAgICAiZGV0ZWN0ZWQgaW4gc29tZSIsCiAgICAgICJkZXRlY3RlZCBpbiBtYW55IiwKICAgICAgImRldGVjdGVkIGluIGFsbCIKICAgICkKICApKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gdGlzc3Vlc19kZXRlY3RlZCwgZmlsbCA9IGRpc3RfY2F0ZWdvcnkpKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZGV0ZWN0aW9uX3BhbGV0dGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZSgKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLCBhZGQgPSAwKSkgKwojICBnZ3RpdGxlKCJOdW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgcGVyIHRpc3N1ZSB0eXBlIikgICsKICBnZ3RpdGxlKCJOdW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgZGV0ZWN0ZWQgaW4gYSBzaW5nbGUgdGlzc3VlIikgICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMiwgYnlyb3cgPSBUUlVFKSkKCmBgYApgYGB7cn0KZGV0ZWN0aW9uX3BhbGV0dGUgPC0gYygiZGV0ZWN0ZWQgaW4gYWxsIiA9ICIjMjUzNDk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgImRldGVjdGVkIGluIG1hbnkiID0gIiMyYzdmYjgiLAogICAgICAgICAgICAgICAgICAgICAgICAiZGV0ZWN0ZWQgaW4gc29tZSIgPSAiIzQxYjZjNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiID0gIiNhMWRhYjQiLAogICAgICAgICAgICAgICAgICAgICAgICAibm90IGRldGVjdGVkICIgPSAiZ3JleSIpCgpvcmRlcmVkX25hbWVzX2Rpc3RyIDwtIGNsYXNzaWZpY2F0aW9uX3Rpc3N1ZSAlPiUgIAogIGZpbHRlcihkaXN0X2NhdGVnb3J5ICVpbiUgYygiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRldGVjdGVkIGluIHNvbWUiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICU+JSAKICBzZXBhcmF0ZV9yb3dzKHRpc3N1ZXNfZGV0ZWN0ZWQsIHNlcCA9ICI7IikgJT4lIAogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JSAKICBjb3VudCgpICU+JSAKICBncm91cF9ieSh0aXNzdWVzX2RldGVjdGVkKSAlPiUgCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShuKSkgJT4lIAogIGFycmFuZ2Uoc3VtKSAlPiUgCiAgLiR0aXNzdWVzX2RldGVjdGVkCgpjbGFzc2lmaWNhdGlvbl90aXNzdWUgJT4lICAKICBmaWx0ZXIoCiAgICBkaXN0X2NhdGVnb3J5ICVpbiUgYygKICAgICAgImRldGVjdGVkIGluIHNpbmdsZSIsCiAgICAgICJkZXRlY3RlZCBpbiBzb21lIgogICAgKQogICkgJT4lCiAgc2VwYXJhdGVfcm93cyh0aXNzdWVzX2RldGVjdGVkLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KGRpc3RfY2F0ZWdvcnksIHRpc3N1ZXNfZGV0ZWN0ZWQpICU+JQogIGNvdW50KCkgJT4lCiAgbXV0YXRlKHRpc3N1ZXNfZGV0ZWN0ZWQgPSBmYWN0b3IodGlzc3Vlc19kZXRlY3RlZCwgb3JkZXJlZF9uYW1lc19kaXN0cikpICU+JQogIG11dGF0ZShkaXN0X2NhdGVnb3J5ID0gZmFjdG9yKAogICAgZGlzdF9jYXRlZ29yeSwKICAgIGMoCiAgICAgICJkZXRlY3RlZCBpbiBzaW5nbGUiLAogICAgICAiZGV0ZWN0ZWQgaW4gc29tZSIsCiAgICAgICJkZXRlY3RlZCBpbiBtYW55IiwKICAgICAgImRldGVjdGVkIGluIGFsbCIKICAgICkKICApKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gdGlzc3Vlc19kZXRlY3RlZCwgZmlsbCA9IGRpc3RfY2F0ZWdvcnkpKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZGV0ZWN0aW9uX3BhbGV0dGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZSgKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLCBhZGQgPSAwKSkgKwogIGdndGl0bGUoIk51bWJlciBvZiBkZXRlY3RlZCBnZW5lcyBwZXIgdGlzc3VlIHR5cGUiKSAgKwogIyBnZ3RpdGxlKCJOdW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgZGV0ZWN0ZWQgaW4gYSBzaW5nbGUgdGlzc3VlIikgICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMiwgYnlyb3cgPSBUUlVFKSkKCmBgYAoKI0ZpZ3VyZSA0CiMjRmlndXJlIDRBIC0gUGllIGNoYXJ0cyBhdCBncm91cGVkIHRpc3N1ZSBsZXZlbApgYGB7cn0KZ2VuZV9jYXRlZ29yeV9wYWwgPC0gYygiRGV0ZWN0ZWQgaW4gYWxsIiA9ICIjMjUzNDk0IiwKICAgICAgICAgICAgICAgICAgICAgICAiRGV0ZWN0ZWQgaW4gbWFueSIgPSAiIzJjN2ZiOCIsCiAgICAgICAgICAgICAgICAgICAgICAgIkRldGVjdGVkIGluIHNvbWUiID0gIiM0MWI2YzQiLAogICAgICAgICAgICAgICAgICAgICAgICJEZXRlY3RlZCBpbiBzaW5nbGUiID0gIiNhMWRhYjQiLAogICAgICAgICAgICAgICAgICAgICAgICJOb3QgZGV0ZWN0ZWQgIiA9ICIJI2JlYmViZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkxvdyB0aXNzdWUgc3BlY2lmaWNpdHkiID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbmhhbmNlZCIgPSAiIzk4NGVhMyIsCiAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwIGVucmljaGVkIiA9ICIjRkY5RDAwIiwKICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIGVucmljaGVkIiA9ICIjZTQxYTFjIikKCnBsb3RfZGF0YSA8LQogIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUgCiAgcmVuYW1lKHNwZWNpZmljaXR5ID0gc3BlY19jYXRlZ29yeSwgZGlzdHJpYnV0aW9uID0gZGlzdF9jYXRlZ29yeSkgJT4lIAogIHNlbGVjdChnZW5lLCBzcGVjaWZpY2l0eSwgZGlzdHJpYnV0aW9uKSAlPiUgCiAgZ2F0aGVyKGNsYXNzX3R5cGUsIGNsYXNzLCAtMSkgJT4lIAogIGdyb3VwX2J5KGNsYXNzX3R5cGUsIGNsYXNzKSAlPiUgCiAgY291bnQoKSAlPiUKICBncm91cF9ieShjbGFzc190eXBlKSAlPiUgCiAgbXV0YXRlKHBlcmMgPSAxMDAgKiBuIC8gc3VtKG4pLAogICAgICAgICBsYWJlbCA9IHBhc3RlMChuLCAiXG4iLCByb3VuZChwZXJjLCAxKSwgIiUiKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKGNsYXNzID0gZmFjdG9yKHN0cl90b19zZW50ZW5jZShjbGFzcyksIHN0cl90b19zZW50ZW5jZShjKCJ0aXNzdWUgZW5yaWNoZWQiLCAiZ3JvdXAgZW5yaWNoZWQiLCAgInRpc3N1ZSBlbmhhbmNlZCIsICJsb3cgdGlzc3VlIHNwZWNpZmljaXR5IiwgImRldGVjdGVkIGluIGFsbCIsICJkZXRlY3RlZCBpbiBtYW55IiwgImRldGVjdGVkIGluIHNvbWUiLCAiZGV0ZWN0ZWQgaW4gc2luZ2xlIiwgIm5vdCBkZXRlY3RlZCIpKSksCiAgICAgICAgIGNsYXNzX3R5cGUgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKGNsYXNzX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIlNwZWNpZmljaXR5IiwgIkRpc3RyaWJ1dGlvbiIpKSkKICAgIApwbG90X2RvZGdlID0gMC4xCnBsb3RfZGF0YSAlPiUgCiAgYXJyYW5nZShtYXRjaChjbGFzcywgcmV2KGxldmVscyhjbGFzcykpKSkgJT4lIAogIGdyb3VwX2J5KGNsYXNzX3R5cGUpICU+JSAKICBtdXRhdGUoeV9zdGFjayA9IGN1bXN1bShuKSAtIG4vMikgJT4lIAogIHtnZ3Bsb3QoLiwgYWVzKDEsIG4sIGZpbGwgPSBjbGFzcywgZ3JvdXAgPSBjbGFzcywgCiAgICAgICAgICAgICAgICAgbGFiZWwgPSBsYWJlbCkpICsKICAgICAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGLCAKICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCAKICAgICAgICAgICAgICAgd2lkdGggPSAxKSArIAogICAgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxLjUgKyBwbG90X2RvZGdlLCB4ZW5kID0gMS41LCAKICAgICAgICAgICAgICAgICAgICAgICB5ID0geV9zdGFjaywgeWVuZCA9IHlfc3RhY2spLCBzaXplID0gMC41KSArCiAgICAgIGdlb21fdGV4dF9yZXBlbChhZXMoeCA9IDEuNSArIHBsb3RfZG9kZ2UsIHkgPSB5X3N0YWNrKSwgCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIG51ZGdlX3ggPSBwbG90X2RvZGdlLCAKICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuc2l6ZSA9IDAuNSwgc2l6ZSA9IDI0LzExKSArCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdlbmVfY2F0ZWdvcnlfcGFsKSArIAogICAgICBmYWNldF93cmFwKH5jbGFzc190eXBlKSArCiAgICAgIGNvb3JkX3BvbGFyKCJ5IixzdGFydCA9IDApICsKICAgICAgdGhlbWVfdm9pZCgpICsgCiAgICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oYygwLDAuOCkpKX0KCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY2xhc3NpZmljYXRpb24vY2xhc3NfcGllc19ncm91cGVkX3Rpc3N1ZS5wZGYiLHdpZHRoID0gNiwgaGVpZ2h0ID0gNSkKYGBgCgojI0ZpZ3VyZSA0QiAtIFNwZWNlZmljaXR5IGZvciBlYWNoIGdyb3VwZWQgdGlzc3VlCmBgYHtyfQpjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lIGdyb3VwX2J5KHNwZWNfY2F0ZWdvcnkpICU+JSBjb3VudCgpIAoKb3JkZXJlZF9uYW1lc19zcCA8LQogIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBmaWx0ZXIoc3BlY19jYXRlZ29yeSAlaW4lIGMoImdyb3VwIGVucmljaGVkIiwgInRpc3N1ZSBlbnJpY2hlZCIsICJ0aXNzdWUgZW5oYW5jZWQiKSkgJT4lCiAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZF90aXNzdWVzLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KHNwZWNfY2F0ZWdvcnksIGVucmljaGVkX3Rpc3N1ZXMpICU+JQogIGNvdW50KCkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShlbnJpY2hlZF90aXNzdWVzKSAlPiUKICBzdW1tYXJpc2Uoc3VtID0gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2Uoc3VtKSAlPiUKICAuJGVucmljaGVkX3Rpc3N1ZXMgJT4lIAogIHN0cl90b19zZW50ZW5jZSgpCiAgCiAgCiAgCgpzcGVjaWZpY2l0eV9wYWxldHRlIDwtIHJldihjKCJOb3QgZGV0ZWN0ZWQiID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTG93IHRpc3N1ZSBzcGVjaWZpY2l0eSIgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbmhhbmNlZCIgPSAiIzk4NGVhMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cCBlbnJpY2hlZCIgPSAiI0ZGOUQwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5yaWNoZWQiID0gIiNlNDFhMWMiKSkKCmNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBmaWx0ZXIoc3BlY19jYXRlZ29yeSAlaW4lIGMoImdyb3VwIGVucmljaGVkIiwgInRpc3N1ZSBlbnJpY2hlZCIsICJ0aXNzdWUgZW5oYW5jZWQiKSkgJT4lCiAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZF90aXNzdWVzLCBzZXAgPSAiOyIpICU+JQogIGdyb3VwX2J5KHNwZWNfY2F0ZWdvcnksIGVucmljaGVkX3Rpc3N1ZXMpICU+JQogIGNvdW50KCkgJT4lCiAgbXV0YXRlKGVucmljaGVkX3Rpc3N1ZXMgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKGVucmljaGVkX3Rpc3N1ZXMpLCBvcmRlcmVkX25hbWVzX3NwKSkgJT4lCiAgbXV0YXRlKHNwZWNfY2F0ZWdvcnkgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKHNwZWNfY2F0ZWdvcnkpLCBjKCJUaXNzdWUgZW5yaWNoZWQiLCAiR3JvdXAgZW5yaWNoZWQiLCAgIlRpc3N1ZSBlbmhhbmNlZCIpKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbiwgeSA9IGVucmljaGVkX3Rpc3N1ZXMsIGZpbGwgPSBzcGVjX2NhdGVnb3J5KSkgKwogIGdlb21fY29sKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHNwZWNpZmljaXR5X3BhbGV0dGUpICsKICB4bGFiKCJOdW1iZXIgb2YgZ2VuZXMiKSsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZSgKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpCiAgKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpICsKICBnZ3RpdGxlKCJTcGVjaWZpY2l0eSBjYXRlZ29yeSBwZXIgY29uc2Vuc3VzIHRpc3N1ZSIpIAoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL2NvbnNlbnN1c190aXNzdWVfc3BlY2lmaWNpdHkucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSA1LjUpCmBgYAoKCiMjRmlndXJlIDRDIC0gU3BlY2lmaWNpdHkgYW5kIFRhdSBzY29yZQpgYGB7cn0KCgpzcGVjaWZpY2l0eV9wYWxldHRlIDwtIHJldihjKCJOb3QgZGV0ZWN0ZWQiID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTG93IHRpc3N1ZSBzcGVjaWZpY2l0eSIgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbmhhbmNlZCIgPSAiIzk4NGVhMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cCBlbnJpY2hlZCIgPSAiI0ZGOUQwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5yaWNoZWQiID0gIiNlNDFhMWMiKSkKCnAxIDwtIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUgCiAgbXV0YXRlKHNwZWNfY2F0ZWdvcnkgPSBmYWN0b3Ioc3RyX3RvX3NlbnRlbmNlKCBzcGVjX2NhdGVnb3J5KSwgbGV2ZWxzID0gbmFtZXMoc3BlY2lmaWNpdHlfcGFsZXR0ZSkpLAogICAgICAgICBlbnJpY2hlZF90aXNzdWVzID0gc3RyX3RvX3NlbnRlbmNlKGVucmljaGVkX3Rpc3N1ZXMpKSAlPiUgCiAgZmlsdGVyKHNwZWNfY2F0ZWdvcnkgIT0gIk5vdCBkZXRlY3RlZCIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB0YXVfc2NvcmUsIHkgPSBzcGVjX2NhdGVnb3J5LCBmaWxsID0gc3BlY19jYXRlZ29yeSkpICsKICBnZW9tX3Zpb2xpbigpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnZW5lX2NhdGVnb3J5X3BhbCwgbmFtZSA9ICJTcGVjaWZpY2l0eSIpICsKICB4bGFiKCJUYXUgc2NvcmUiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgCgojZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NsYXNzaWZpY2F0aW9uL3RhdV90b19zcGVjaWZpY2l0eS5wZGYiLHdpZHRoID0gNS41LCBoZWlnaHQgPSA0KQoKCnAyIDwtIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBnZ3Bsb3QoYWVzKHRhdV9zY29yZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB5bGFiKCJDb3VudCIpKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoImdyYXk5MCIpLAogICAgICAgICNheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMCwgYWRkID0gMCkpCgpwMi8gcDEgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAzKSkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9zcGVjaWZpY2l0eV9hbmRfZGVuZHJvZ3JhbV9jb25zZW5zdXNfd19vdmVyYWxsLnBkZiIsd2lkdGggPSA3LCBoZWlnaHQgPSA2KQoKYGBgCgojI0ZpZ3VyZSA0RCAtIEdlbmUgYW5ub3RhdGlvbiBhbGx1dmlhbCBwbG90CmBgYHtyfQp3aWR0aCA9IDAuMQoKYWxsdXZfMSA8LQogIAogIGNsYXNzaWZpY2F0aW9uX2NvbnNlbnN1cyAlPiUKICBtdXRhdGUoU3BlY2lmaWNpdHkgPSBzdHJfdG9fc2VudGVuY2Uoc3BlY19jYXRlZ29yeSksCiAgICAgICAgICBEaXN0cmlidXRpb24gPSBzdHJfdG9fc2VudGVuY2UoZGlzdF9jYXRlZ29yeSkpICU+JQogIHNlbGVjdChTcGVjaWZpY2l0eSwKICAgICAgICAgRGlzdHJpYnV0aW9uKSAlPiUKICBtdXRhdGUocm93X24gPSByb3dfbnVtYmVyKCkpICU+JQogIGdhdGhlcihiYXIsIGNodW5rLCAtcm93X24pICU+JQogIG11dGF0ZShjb2xvcl92YXJzID0gMSkgJT4lCiAgZ3JvdXBfYnkocm93X24pICU+JQogIG11dGF0ZShjaHVua19jb2xvciA9IGNodW5rW21hdGNoKGMoIlNwZWNpZmljaXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaXN0cmlidXRpb24iKVtjb2xvcl92YXJzXSwgYmFyKV0pICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgCiAgCiAgbXV0YXRlKGNodW5rID0gZmFjdG9yKGNodW5rLCBsZXZlbHMgPSBjKCdUaXNzdWUgZW5yaWNoZWQnLCAnR3JvdXAgZW5yaWNoZWQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1Rpc3N1ZSBlbmhhbmNlZCcsICdMb3cgdGlzc3VlIHNwZWNpZmljaXR5JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdEZXRlY3RlZCBpbiBzaW5nbGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnRGV0ZWN0ZWQgaW4gc29tZScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnRGV0ZWN0ZWQgaW4gbWFueScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnRGV0ZWN0ZWQgaW4gYWxsJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ05vdCBkZXRlY3RlZCcpKSwKICAgICAgICAgYmFyID0gZmFjdG9yKGJhciwgbGV2ZWxzID0gYygiU3BlY2lmaWNpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaXN0cmlidXRpb24iKSkpICU+JQogIAogIAogIGdncGxvdChhZXMoeCA9IGJhciwgc3RyYXR1bSA9IGNodW5rLCBhbGx1dml1bSA9IHJvd19uLAogICAgICAgICAgICAgeSA9IDEpKSArCiAgCiAgZ2VvbV9mbG93KGFlcyhmaWxsID0gY2h1bmtfY29sb3IpLCAKICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCB3aWR0aCA9IHdpZHRoLAogICAgICAgICAgICBrbm90LnBvcyA9IDEvNikgKwogIGdlb21fc3RyYXR1bShhZXMoZmlsbCA9IGNodW5rKSwgCiAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRiwgY29sb3IgPSBOQSwgd2lkdGggPSB3aWR0aCkgKwogIAogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gYyguMSwgLjEpLCBwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhnZW5lX2NhdGVnb3J5X3BhbCkpICsgCiAgCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGNvb3JkX2ZsaXAoKQoKIyBhbGx1dl8xCgpmbG93X2RhdGEgPC0KICBnZ3Bsb3RfYnVpbGQoYWxsdXZfMSkkZGF0YVtbMV1dICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHsKICAgIGlmICgic2lkZSIgJWluJSBuYW1lcyguKSkgewogICAgICAuCiAgICB9IGVsc2V7CiAgICAgIG11dGF0ZSguLAogICAgICAgICAgICAgc2lkZSA9IGNhc2Vfd2hlbihmbG93ID09ICJmcm9tIiB+ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsb3cgPT0gInRvIiB+ICJlbmQiKSkKICAgIH0KICB9CgoKCnN0cmF0dW1fZGF0YSA8LSAKICBnZ3Bsb3RfYnVpbGQoYWxsdXZfMSkkZGF0YVtbMl1dCgpmbG93X2RhdGFfbGFiZWxzIDwtCiAgZmxvd19kYXRhICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHNlbGVjdCh4LCBzdHJhdHVtLCBncm91cCwgc2lkZSwgeW1pbiwgeW1heCkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNpZGUsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBjKHgsIHN0cmF0dW0sIHltaW4sIHltYXgpKSAlPiUKICBtdXRhdGVfYXQoCiAgICBjKAogICAgICAieF9lbmQiLAogICAgICAieW1heF9lbmQiLAogICAgICAieW1pbl9lbmQiLAogICAgICAieF9zdGFydCIsCiAgICAgICJ5bWF4X3N0YXJ0IiwKICAgICAgInltaW5fc3RhcnQiCiAgICApLAogICAgYXMubnVtZXJpYwogICkgJT4lCiAgZ3JvdXBfYnkoc3RyYXR1bV9zdGFydCwgc3RyYXR1bV9lbmQsIHhfc3RhcnQsIHhfZW5kKSAlPiUKICBzdW1tYXJpc2UoCiAgICB5X2VuZCA9IChtaW4oeW1pbl9lbmQpICsgbWF4KHltYXhfZW5kKSkgLyAyLAogICAgeV9zdGFydCA9IChtaW4oeW1pbl9zdGFydCkgKyBtYXgoeW1heF9zdGFydCkpIC8gMiwKICAgIHNpemUgPSBtYXgoeW1heF9zdGFydCkgLSBtaW4oeW1pbl9zdGFydCkKICApCgphbGx1dl8yIDwtIAogIGFsbHV2XzEgKwogIGdlb21fdGV4dChkYXRhID0gZmxvd19kYXRhX2xhYmVscywKICAgICAgICAgICAgYWVzKHggPSB4X3N0YXJ0ICsgd2lkdGgvMiwKICAgICAgICAgICAgICAgIHkgPSB5X3N0YXJ0LCAKICAgICAgICAgICAgICAgIGxhYmVsID0gc2l6ZSksIAogICAgICAgICAgICBpbmhlcml0LmFlcyA9IEYsIAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgYW5nbGUgPSAtOTAsCiAgICAgICAgICAgIGhqdXN0ID0gMSwKICAgICAgICAgICAgdmp1c3QgPSAwLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGZsb3dfZGF0YV9sYWJlbHMsCiAgICAgICAgICAgIGFlcyh4ID0geF9lbmQgLSB3aWR0aC8yLAogICAgICAgICAgICAgICAgeSA9IHlfZW5kLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gc2l6ZSksIAogICAgICAgICAgICBpbmhlcml0LmFlcyA9IEYsIAogICAgICAgICAgICBzaXplID0gMywgCiAgICAgICAgICAgIGFuZ2xlID0gLTkwLAogICAgICAgICAgICBoanVzdCA9IDAsCiAgICAgICAgICAgIHZqdXN0ID0gMC41KSArCiAgCiAgIyBTdHJhdHVtIGxhYmVsCiAgCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdHJhdHVtX2RhdGEgJT4lCiAgICAgICAgICAgICAgZmlsdGVyKHggPT0gMSksCiAgICAgICAgICAgIGFlcyh4ID0geCAtIHdpZHRoLzIsCiAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgIGxhYmVsID0gc3RyYXR1bSksCiAgICAgICAgICAgIHNpemUgPSA0LCAKICAgICAgICAgICAgdmp1c3QgPSAxLjUsCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRikgKyAKICBnZW9tX3RleHQoZGF0YSA9IHN0cmF0dW1fZGF0YSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeCA9PSAyKSwKICAgICAgICAgICAgYWVzKHggPSB4ICsgd2lkdGgvMiwKICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgbGFiZWwgPSBzdHJhdHVtKSwKICAgICAgICAgICAgc2l6ZSA9IDQsIAogICAgICAgICAgICB2anVzdCA9IC0wLjUsCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRikgKyAKICAKICBnZW9tX3RleHQoZGF0YSA9IHN0cmF0dW1fZGF0YSwKICAgICAgICAgICAgYWVzKHggPSB4LCAKICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgbGFiZWwgPSB5bWF4IC0geW1pbiksIAogICAgICAgICAgICBzaXplID0gNCwgCiAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRikKCgphbGx1dl8yCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9hbGx1dmlhbF9jbGFzc2lmaWNhdGlvbi5wZGYiLHdpZHRoID0gOCwgaGVpZ2h0ID0gMykKCmBgYAojIyNFeHRyYSBmaWd1cmVzIG5vdCBpbiByZXBvcnQKYGBge3J9Cm9yZ2FuX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KGNvbnNlbnN1c190aXNzdWVfbmFtZSwgY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcikgJT4lIHVuaXF1ZSgpCnBhbCA8LSAgb3JnYW5fY29sb3JzJGNvbnNlbnN1c190aXNzdWVfY29sb3IKcGFsIDwtIHNldE5hbWVzKHBhbCwgc3RyX3RvX3NlbnRlbmNlKG9yZ2FuX2NvbG9ycyRjb25zZW5zdXNfdGlzc3VlX25hbWUpKQoKCnNwZWNpZmljaXR5X3BhbGV0dGUgPC0gcmV2KGMoIk5vdCBkZXRlY3RlZCIgPSAiZ3JleSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb3cgdGlzc3VlIHNwZWNpZmljaXR5IiA9ICJncmV5NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIGVuaGFuY2VkIiA9ICIjOTg0ZWEzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwIGVucmljaGVkIiA9ICIjRkY5RDAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRpc3N1ZSBlbnJpY2hlZCIgPSAiI2U0MWExYyIpKQoKCmNsYXNzX3RhYmxlX3RlbXAgPC0KICBjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lCiAgc2VsZWN0KGdlbmUsIHNwZWNfY2F0ZWdvcnksIGVucmljaGVkX3Rpc3N1ZXMpICU+JQogIHNlcGFyYXRlX3Jvd3MoZW5yaWNoZWRfdGlzc3Vlcywgc2VwID0gIjsiKSAlPiUKICBtdXRhdGUoc3BlY19jYXRlZ29yeSA9IGZhY3RvcihzdHJfdG9fc2VudGVuY2UoIHNwZWNfY2F0ZWdvcnkpLCBsZXZlbHMgPSBuYW1lcyhzcGVjaWZpY2l0eV9wYWxldHRlKSksCiAgICAgICAgIGVucmljaGVkX3Rpc3N1ZXMgPSBzdHJfdG9fc2VudGVuY2UoZW5yaWNoZWRfdGlzc3VlcykpCgpwbG90X2RlbmRybyA8LQogIHRtbV9jb25zZW5zdXNfdGlzc3VlICU+JSAKICBzZWxlY3QodGFyZ2V0X2lkLCBjb25zZW5zdXNfdGlzc3VlX25hbWUsIHRtbSkgJT4lIAogIG11dGF0ZShjb25zZW5zdXNfdGlzc3VlX25hbWUgPSBzdHJfdG9fc2VudGVuY2UoY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSkgJT4lCiAgc3ByZWFkKHRhcmdldF9pZCwgdG1tKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoImNvbnNlbnN1c190aXNzdWVfbmFtZSIpICAlPiUKICB0KCkgJT4lCiAgY29yKG1ldGhvZCA9ICJzcGVhcm1hbiIpICU+JQogIAogIHsxIC0gLn0gJT4lCiAgYXMuZGlzdCgpICU+JSAKICBoY2x1c3QobWV0aG9kID0gImF2ZXJhZ2UiKSAlVD4lCiAgcGxvdCAlPiUKICBkZW5kcm9fZGF0YSgpCiAgCiAgCmRlbmRyb19wbG90X2RhdGEgPC0gCiAgbGVmdF9qb2luKHBsb3RfZGVuZHJvJHNlZ21lbnRzLCAKICAgICAgICAgICAgcGxvdF9kZW5kcm8kbGFiZWxzLCAKICAgICAgICAgICAgYnkgPSBjKCJ4IiA9ICJ4IiwgInllbmQiID0gInkiKSkgCgpsZWZ0X3Bsb3QgPC0gCiAgZGVuZHJvX3Bsb3RfZGF0YSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4PXksIHk9eCwgeGVuZD15ZW5kLCB5ZW5kPXhlbmQsIGdyb3VwID0gbGFiZWwpKSsKICBnZW9tX3JlY3QoYWVzKHhtaW49MCwgeW1pbj14ICsgMC41LCAKICAgICAgICAgICAgICAgIHhtYXg9LTAuMDIsIHltYXg9eGVuZCAtIDAuNSwgCiAgICAgICAgICAgICAgICBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSsKICBzY2FsZV94X3JldmVyc2UoZXhwYW5kID0gZXhwYW5zaW9uKDApLCBwb3NpdGlvbiA9ICJ0b3AiKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKCkpICsKICB4bGFiKCIxIC0gU3BlYXJtYW4ncyByaG8iKSArCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCB1bml0cyA9ICJtbSIpLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSAKCnJpZ2h0X3Bsb3QgPC0gCiAgY2xhc3NfdGFibGVfdGVtcCAlPiUKICBmaWx0ZXIoIWlzLm5hKGVucmljaGVkX3Rpc3N1ZXMpKSAlPiUKICBncm91cF9ieShlbnJpY2hlZF90aXNzdWVzLCBzcGVjX2NhdGVnb3J5KSAlPiUKICBzdW1tYXJpc2Uobl9nZW5lcyA9IG4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShlbnJpY2hlZF90aXNzdWVzID0gZmFjdG9yKGVucmljaGVkX3Rpc3N1ZXMsIGxldmVscyA9IHBsb3RfZGVuZHJvJGxhYmVscyRsYWJlbCksCiAgICAgICAgIHNwZWNfY2F0ZWdvcnkgPSBmYWN0b3Ioc3BlY19jYXRlZ29yeSwgbmFtZXMoc3BlY2lmaWNpdHlfcGFsZXR0ZSkpKSAlPiUKICBnZ3Bsb3QoYWVzKG5fZ2VuZXMsIGVucmljaGVkX3Rpc3N1ZXMsIGZpbGwgPSBzcGVjX2NhdGVnb3J5KSkgKwogIGdlb21fY29sKHdpZHRoID0gMC44LCBzaXplID0gMC4xKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBnZW5lX2NhdGVnb3J5X3BhbCwgbmFtZSA9ICJTcGVjaWZpY2l0eSIpICsKICAjIGNvb3JkX2ZsaXAoKSArCiAgeGxhYigiTnVtYmVyIG9mIGdlbmVzIikgKwogIAogIHNjYWxlX3hfY29udGludW91cyhwb3NpdGlvbiA9ICJ0b3AiLCBleHBhbmQgPSBleHBhbnNpb24oYygwLCAwLjEpKSkgKyAKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbigpKSArCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCAKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNywgMC41KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQoKbGVmdF9wbG90ICsgcmlnaHRfcGxvdCArCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygwLjMsIDEpKQoKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9jbGFzc2lmaWNhdGlvbi9zcGVjaWZpY2l0eV9hbmRfZGVuZHJvZ3JhbV9jb25zZW5zdXMucGRmIix3aWR0aCA9IDcsIGhlaWdodCA9IDcpCgpgYGAKI0ZpZ3VyZSA1CmBgYHtyfQojI1Bsb3QgYmFzZWQgb24gTWF4IEthcmxzc29uJ3MgbmV0d29yayBwbG90CgpjbGFzc2lmaWNhdGlvbl9uZXR3b3JrX3Bsb3RfMiA8LSAKICBmdW5jdGlvbihjbGFzc190YWJsZSwgZ2VuZV9jb2wsIHNwZWNfY29sLCBlbnJpY2hlZF9jb2wsIHNwZWNfZmlsdGVyLCBwYWwsIHNhdmVuYW1lLCBlbnJpY2hlZF9zZXAgPSAiOyIsIAogICAgICAgICAgIG5vZGVfZmlsdGVyX3JhbmsgPSAyLCBub2RlX2ZpbHRlcl9zaG93X2NhdCA9IGMoInRpc3N1ZSBlbnJpY2hlZCIpLCAKICAgICAgICAgICBub2RlX2ZpbHRlcl9taW4gPSAyLCBub2RlX2ZpbHRlcl9uX3Nob3cgPSA4LCBzY2FsZV9mYWN0b3IgPSAxKSB7CiAgICAKICAgIGVucmljaG1lbnRfdGFibGUgPC0gCiAgICAgIGNsYXNzX3RhYmxlICU+JSAKICAgICAgc2VsZWN0KGdlbmUgPSBnZW5lX2NvbCwgCiAgICAgICAgICAgICBzcGVjID0gc3BlY19jb2wsIAogICAgICAgICAgICAgZW5yaWNoZWQgPSBlbnJpY2hlZF9jb2wpICU+JSAKICAgICAgZmlsdGVyKHNwZWMgJWluJSBzcGVjX2ZpbHRlcikgJT4lIAogICAgICBncm91cF9ieShlbnJpY2hlZCwgc3BlYykgJT4lIAogICAgICBzdW1tYXJpc2Uobl9nZW5lcyA9IG4oKSkgJT4lCiAgICAgIHVuZ3JvdXAoKQogICAgCiAgICBuZXRfZGF0YSA8LQogICAgICBlbnJpY2htZW50X3RhYmxlICU+JSAKICAgICAgbXV0YXRlKGFsbF9lbnJpY2hlZCA9IGVucmljaGVkKSAlPiUKICAgICAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZCwgc2VwID0gZW5yaWNoZWRfc2VwKSAlPiUKICAgICAgZ3JvdXBfYnkoZW5yaWNoZWQsIHNwZWMpICU+JSAKICAgICAgbXV0YXRlKHJhbmsgPSByYW5rKC1uX2dlbmVzLCB0aWVzLm1ldGhvZCA9ICJtaW4iKSkgJT4lCiAgICAgIGdyb3VwX2J5KGFsbF9lbnJpY2hlZCkgJT4lCiAgICAgIG11dGF0ZShhbnlfbG93X3JhbmsgPSBhbnkocmFuayA8PSBub2RlX2ZpbHRlcl9yYW5rKSkgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgCiAgICAgIGZpbHRlcigoc3BlYyA9PSBub2RlX2ZpbHRlcl9zaG93X2NhdCB8IGFueV9sb3dfcmFuayB8IG5fZ2VuZXMgPj0gbm9kZV9maWx0ZXJfbl9zaG93KSAmCiAgICAgICAgICAgICAgIChzcGVjID09IG5vZGVfZmlsdGVyX3Nob3dfY2F0IHwgbl9nZW5lcyA+PSBub2RlX2ZpbHRlcl9taW4pKSAlPiUgCiAgICAgIG11dGF0ZShlZGdlX2lkID0gcGFzdGUoImVucmljaGVkOiIsIGFsbF9lbnJpY2hlZCkpICU+JSAKICAgICAgYXJyYW5nZShuX2dlbmVzKQogICAgCiAgICBuZXRfZWRnZXMgPC0gCiAgICAgIG5ldF9kYXRhICUkJSAKICAgICAgdGliYmxlKG5vZGUxID0gZW5yaWNoZWQsIG5vZGUyID0gZWRnZV9pZCwgbiA9IG5fZ2VuZXMpICU+JSAKICAgICAgdW5pcXVlKCkKICAgIAogICAgZyA8LQogICAgICBuZXRfZWRnZXMgJT4lCiAgICAgIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkaXJlY3RlZCA9IEZBTFNFKSAlPiUKICAgICAgZ2dyYXBoKGxheW91dCA9ICJrayIpIAogICAgCiAgICBsaW5rX21hcCA8LSAKICAgICAgbmV0X2VkZ2VzICU+JSAKICAgICAgZ2F0aGVyKG5vZGUsIGlkLCAtKDMpKSAlPiUgCiAgICAgIG11dGF0ZSh0aXNzdWVfbm9kZSA9IG5vZGUgPT0gIm5vZGUxIiwgCiAgICAgICAgICAgICBjb2xvcl9pZCA9IGNhc2Vfd2hlbih0aXNzdWVfbm9kZSB+IGlkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCI7IiwgaWQpIH4gIkdyb3VwIGVucmljaGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFncmVwbCgiOyIsIGlkKSB+ICJUaXNzdWUgZW5yaWNoZWQiKSwgCiAgICAgICAgICAgICBsYWJlbCA9IGlmZWxzZSh0aXNzdWVfbm9kZSwgY29sb3JfaWQsIG4pKSAlPiUKICAgICAgc2VsZWN0KG4sIG5vZGUsIGlkLCB0aXNzdWVfbm9kZSwgY29sb3JfaWQsIGxhYmVsKSAlPiUKICAgICAgdW5pcXVlKCkKICAgIAogICAgCiAgICBlZGdlX2RhdGEgPC0gZ2V0X2VkZ2VzKCkoZyRkYXRhKQogICAgbm9kZV9kYXRhIDwtIAogICAgICBnZXRfbm9kZXMoKShnJGRhdGEpICU+JSAKICAgICAgYXNfdGliYmxlKCkgJT4lCiAgICAgIGxlZnRfam9pbihsaW5rX21hcCwgCiAgICAgICAgICAgICAgICBieSA9IGMoIm5hbWUiID0gImlkIikpIAogICAgCiAgICAKICAgIGZpZyA8LSAKICAgICAgZyArIAogICAgICBnZW9tX2VkZ2VfYXJjKGFlcyh3aWR0aCA9IG4pLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmF5IiwgCiAgICAgICAgICAgICAgICAgICAgc3RyZW5ndGggPSAwLCAKICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpICsgCiAgICAgIHNjYWxlX2VkZ2VfYWxwaGFfY29udGludW91cyhyYW5nZSA9IGMoMC4zLCAxKSkgKwogICAgICBzY2FsZV9lZGdlX3dpZHRoX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDMpKSArCiAgICAgIAogICAgICBnZW9tX25vZGVfcG9pbnQoZGF0YSA9IG5vZGVfZGF0YSAgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighdGlzc3VlX25vZGUpLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHNpemUgPSBsb2cxMChuKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGNvbG9yX2lkKSwgCiAgICAgICAgICAgICAgICAgICAgICBzdHJva2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgIyBzaXplID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDIxLAogICAgICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSsKICAgICAgZ2VvbV9ub2RlX3BvaW50KGRhdGEgPSBub2RlX2RhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih0aXNzdWVfbm9kZSksCiAgICAgICAgICAgICAgICAgICAgICBhZXMoZmlsbCA9IGNvbG9yX2lkKSwgCiAgICAgICAgICAgICAgICAgICAgICBzdHJva2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDIwICogc2NhbGVfZmFjdG9yLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwKICAgICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrCiAgICAgIGdlb21fbm9kZV90ZXh0KGRhdGEgPSBub2RlX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDQgKiBzY2FsZV9mYWN0b3IpICsKICAgICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYyg1LCAxMCkgKiBzY2FsZV9mYWN0b3IpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSArCiAgICAgIAogICAgICB0aGVtZV92b2lkKCkKICAgIAogICAgIyMgLS0tLS0gU2F2ZQogICAgCiAgICAKICAgIGN5dG9fc3VtbWFyeSA8LSAKICAgICAgbmV0X2VkZ2VzICU+JSAKICAgICAgbXV0YXRlKGNhdGVnb3J5ID0gaWZlbHNlKCFncmVwbChlbnJpY2hlZF9zZXAsIG5vZGUyKSwgIlRpc3N1ZSBlbnJpY2hlZCIsICJHcm91cCBlbnJpY2hlZCIpLCAKICAgICAgICAgICAgIG5vZGVfaWQgPSB1bmNsYXNzKGZhY3Rvcihub2RlMikpLAogICAgICAgICAgICAgbm9kZTEgPSBzdHJfdG9fc2VudGVuY2Uobm9kZTEpLCAKICAgICAgICAgICAgIG5fc3FydCA9IHNxcnQobiksIAogICAgICAgICAgICAgc3RyX2xlbiA9IHN0cl9sZW5ndGgobm9kZTEpKSAlPiUKICAgICAgc2VsZWN0KGNhdGVnb3J5LCBub2RlMSwgbm9kZTIsIG5vZGVfaWQsIG4sIG5fc3FydCwgc3RyX2xlbikgCiAgICAKICAgIGN5dG9fc3VtbWFyeSAlPiUgCiAgICAgIHdyaXRlX2RlbGltKCJjeXRvc2NhcGUgbm9kZXMgc3VtbWFyeS50eHQiLCBkZWxpbSA9ICJcdCIpCiAgIyAgICB3cml0ZV9kZWxpbShzYXZlcGF0aChwYXN0ZShzYXZlbmFtZSwgImN5dG9zY2FwZSBub2RlcyBzdW1tYXJ5LnR4dCIpKSwgZGVsaW0gPSAiXHQiKQogICAgCiAgICBiaW5kX3Jvd3MoY3l0b19zdW1tYXJ5ICU+JSAKICAgICAgICAgICAgICAgIGxlZnRfam9pbihwYWwgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5mcmFtZSgibm9kZTEiLCAiY29sb3IiKSkgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KG5vZGVfaWQgPSBub2RlMSwgCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3IpICU+JSAKICAgICAgICAgICAgICAgIHVuaXF1ZSgpICU+JQogICAgICAgICAgICAgICAgbXV0YXRlKG5vZGVfdHlwZSA9ICJUaXNzdWUiKSwKICAgICAgICAgICAgICBjeXRvX3N1bW1hcnkgJT4lIAogICAgICAgICAgICAgICAgbXV0YXRlKGNvbG9yID0gY2FzZV93aGVuKGNhdGVnb3J5ID09ICJUaXNzdWUgZW5yaWNoZWQiIH4gIiNlNDFhMWMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3J5ID09ICJHcm91cCBlbnJpY2hlZCIgfiAiI0ZGOUQwMCIpLCAKICAgICAgICAgICAgICAgICAgICAgICBub2RlX2lkID0gYXMuY2hhcmFjdGVyKG5vZGVfaWQpKSAlPiUKICAgICAgICAgICAgICAgIHNlbGVjdChub2RlX2lkLCBjb2xvcikgJT4lIAogICAgICAgICAgICAgICAgdW5pcXVlKCkgJT4lCiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIG11dGF0ZShub2RlX3R5cGUgPSAiRW5yaWNobWVudCIpKSAlPiUKICAgICAgbXV0YXRlKGNvbG9yMiA9IGNhc2Vfd2hlbihub2RlX3R5cGUgPT0gIkVucmljaG1lbnQiIH4gY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZV90eXBlID09ICJUaXNzdWUiIH4gIiNEM0QzRDNGRiIpLAogICAgICAgICAgICAgY29sb3IzID0gY2FzZV93aGVuKG5vZGVfdHlwZSA9PSAiRW5yaWNobWVudCIgfiBjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlX3R5cGUgPT0gIlRpc3N1ZSIgfiAiI0JFQkVCRUZGIikpICU+JSAKICAgICAgCiAgICAgIHdyaXRlX2RlbGltKCJjeXRvc2NhcGUgbm9kZXMgY29sb3IudHh0IiwgZGVsaW0gPSAiXHQiKSAKICAgICAgI3dyaXRlX2RlbGltKHNhdmVwYXRoKHBhc3RlKHNhdmVuYW1lLCAiY3l0b3NjYXBlIG5vZGVzIGNvbG9yLnR4dCIpKSwgZGVsaW0gPSAiXHQiKSAKICAgIAogICAgYmluZF9yb3dzKGN5dG9fc3VtbWFyeSAlPiUgCiAgICAgICAgICAgICAgICBzZWxlY3Qobm9kZV9pZCA9IG5vZGUxKSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUobGFiZWwgPSBub2RlX2lkKSAlPiUKICAgICAgICAgICAgICAgIHVuaXF1ZSgpLAogICAgICAgICAgICAgIGN5dG9fc3VtbWFyeSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUobm9kZV9pZCA9IGFzLmNoYXJhY3Rlcihub2RlX2lkKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBhcy5jaGFyYWN0ZXIobikpICU+JQogICAgICAgICAgICAgICAgc2VsZWN0KG5vZGVfaWQsIGxhYmVsKSAlPiUgCiAgICAgICAgICAgICAgICB1bmlxdWUoKSkgJT4lIAogICAgICB3cml0ZV9kZWxpbSgiY3l0b3NjYXBlIG5vZGVzIGxhYmVsIHdob2xlIGdyb3VwLnR4dCIsCiAgICAgICN3cml0ZV9kZWxpbShzYXZlcGF0aChwYXN0ZShzYXZlbmFtZSwgImN5dG9zY2FwZSBub2RlcyBsYWJlbCB3aG9sZSBncm91cC50eHQiKSksIAogICAgICAgICAgICAgICAgICBkZWxpbSA9ICJcdCIpCiAgICAKICAgICMjIC0tLS0KICAgIAogICAgZmlnCiAgfQpgYGAKCmBgYHtyfQoKCm9yZ2FuX2NvbG9ycyA8LSBtZXRhZGF0YSAlPiUgc2VsZWN0KGNvbnNlbnN1c190aXNzdWVfbmFtZSwgb3JnYW5fY29sb3IpICU+JSB1bmlxdWUoKQpwYWwxIDwtICBvcmdhbl9jb2xvcnMkb3JnYW5fY29sb3IKcGFsMSA8LSBzZXROYW1lcyhwYWwxLCBvcmdhbl9jb2xvcnMkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKQoKc3BlY2lmaWNpdHlfcGFsZXR0ZSA8LSByZXYoYygiTm90IGRldGVjdGVkIiA9ICJncmV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIiA9ICJncmV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxvdyB0aXNzdWUgc3BlY2lmaWNpdHkiID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaXNzdWUgZW5oYW5jZWQiID0gIiM5ODRlYTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAgZW5yaWNoZWQiID0gIiNGRjlEMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiVGlzc3VlIGVucmljaGVkIiA9ICIjZTQxYTFjIikpCgpsaWJyYXJ5KGluZmx1ZW50aWFsKQoKY2xhc3NpZmljYXRpb25fbmV0d29ya19wbG90XzIoCiAgY2xhc3NfdGFibGUgPSBjbGFzc2lmaWNhdGlvbl9jb25zZW5zdXMgJT4lIG11dGF0ZShzcGVjX2NhdGVnb3J5ID0gc3RyX3RvX3NlbnRlbmNlKHNwZWNfY2F0ZWdvcnkpKSwKICBnZW5lX2NvbCA9ICJnZW5lIiwKICBzcGVjX2NvbCA9ICJzcGVjX2NhdGVnb3J5IiwKICBlbnJpY2hlZF9jb2wgPSAiZW5yaWNoZWRfdGlzc3VlcyIsCiAgc3BlY19maWx0ZXIgPSBjKCJUaXNzdWUgZW5yaWNoZWQiLCAiR3JvdXAgZW5yaWNoZWQiKSwKICBwYWwgPSBjKHBhbDEsIHNwZWNpZmljaXR5X3BhbGV0dGUpLAogIHNhdmVuYW1lID0gInRlc3RfaW50ZXJjb25zZW5zdXMiLAogIGVucmljaGVkX3NlcCA9ICI7IiwKICBub2RlX2ZpbHRlcl9yYW5rID0gMiwKICBub2RlX2ZpbHRlcl9zaG93X2NhdCA9IGMoIlRpc3N1ZSBlbnJpY2hlZCIpLAogIG5vZGVfZmlsdGVyX21pbiA9IDIsCiAgbm9kZV9maWx0ZXJfbl9zaG93ID0gNSwKICBzY2FsZV9mYWN0b3IgPSAwLjgKKSAKCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvZGF0YV9wcmVzZW50YXRpb24vbmV3b3JrX3Bsb3QyLWZpbHRlci5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCkKYGBgCgojUmVhZCBDb21wYXJpc29uIGRhdGEKYGBge3J9CmNvbXBfbWV0YWRhdGEgPC0gcmVhZF9jc3YoIi4vZGF0YS9maW5hbF9kYXRhL2NvbXBhcmlzb25fbWV0YWRhdGEtaW5pdC0xLTAuY3N2IikKCmh1bWFuX2F0bGFzX3Rpc3N1ZSA8LQogIHJlYWRfZGVsaW0oIi4vZGF0YS9odW1hbl9ocGEvcm5hX3Rpc3N1ZV9jb25zZW5zdXMudHN2IiwgZGVsaW0gPSAiXHQiKSAlPiUKICBtdXRhdGUoc291cmNlID0gImhwYV9jb25zZW5zdXMiKSAlPiUKICBncm91cF9ieShHZW5lKSAlPiUKICBtdXRhdGUobnggPSBjYXNlX3doZW4oblRQTSA9PSAwIH4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgVCB+IG5UUE0gLyBzcXJ0KHNkKG5UUE0pKSkpICU+JQogIHVuZ3JvdXAoKSAjJT4lIAogICMgIyBqdXN0IG5lY2Vzc2FyeSBpZiByZWFkIGRhdGEgb2YgbXVsdGlwbGUgZGlmZmVyZW50IHNvdXJjZXMsIHRoZW4gd291bGQgbWFrZSBzZW5zZSB0byBhdmVyYWdlIG91dCBzYW1wbGVzIG5hbWVkIHRoZSBzYW1lCiAgIyAjIGp1c3QgZnJvbSBvbmUgc291cmNlIGF0bSwgc28gbm8gbmVlZCBmb3IgdGhpcy4KICAjIGdyb3VwX2J5KFRpc3N1ZSwgR2VuZSkgJT4lCiAgIyBzdW1tYXJpc2UoblRQTSA9IG1lYW4oblRQTSwgbmEucm0gPSBUKSkgJT4lIAogICMgdW5ncm91cCgpCgpocGFfY29tcCA8LSBodW1hbl9hdGxhc190aXNzdWUgJT4lIGxlZnRfam9pbigKICBjb21wX21ldGFkYXRhICU+JQogICAgc2VsZWN0KGhwYV9jb25zZW5zdXNfbmFtZSwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lCiAgICBkaXN0aW5jdCgpICU+JQogICAgZmlsdGVyKCFpcy5uYShocGFfY29uc2Vuc3VzX25hbWUpKSAlPiUKICAgIGZpbHRlcighaXMubmEoY29tcGFyaXNvbl90aXNzdWVfbmFtZSkpLAogIGJ5ID0gYygiVGlzc3VlIiA9ICJocGFfY29uc2Vuc3VzX25hbWUiKSkgJT4lIAogIGZpbHRlcighaXMubmEoY29tcGFyaXNvbl90aXNzdWVfbmFtZSkpICU+JSAKICBncm91cF9ieShHZW5lLGNvbXBhcmlzb25fdGlzc3VlX25hbWUpICU+JSAKICBzdW1tYXJpc2UodG1tID0gbWF4KG5UUE0pLAogICAgICAgICAgICBueCA9IG1heChueCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHJlbmFtZSh0aXNzdWUgPSBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKQoKcmF0X3Rpc3N1ZSA8LQogIHJlYWRfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9maW5hbF90bW1fdGlzc3VlX25hbWUuY3N2IikgJT4lIAogICMgI29taXQgZ2F0aGVyLCBkYXRhIHNob3VsZCBiZSAgYWxyZWFkeSBpbiBsb25nIGZvcm1hdAogICMgZ2F0aGVyKHNhbXBsZSwgdG1tLC0xKSAlPiUKICBncm91cF9ieSh0YXJnZXRfaWQpICU+JQogIG11dGF0ZShueCA9IGNhc2Vfd2hlbih0bW0gPT0gMCB+IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIFQgfiB0bW0gLyBzcXJ0KHNkKHRtbSkpKSkgJT4lCiAgdW5ncm91cCgpCiAgCgojIHNlbGVjdCB0aXNzdWVzIGFuZCBwb29sIHRpc3N1ZXMgdGhhdCB3aWxsIGdldCBjb21wYXJlZApyYXRfdGlzc3VlX2NvbXAgPC0gcmF0X3Rpc3N1ZSAlPiUgbGVmdF9qb2luKGNvbXBfbWV0YWRhdGEgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHRpc3N1ZV9uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJ0aXNzdWVfbmFtZSIgPSAidGlzc3VlX25hbWUiKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSkgJT4lIAogIGdyb3VwX2J5KHRhcmdldF9pZCwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lIAogIHN1bW1hcmlzZSh0bW0gPSBtYXgodG1tKSwKICAgICAgICAgICAgbnggPSBtYXgobngpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICByZW5hbWUodGlzc3VlID0gY29tcGFyaXNvbl90aXNzdWVfbmFtZSkKCiNjaGVjayBpZiBhbGwgaGF2ZSB0aGUgc2FtZSB0aXNzdWUKYWxsKHJhdF90aXNzdWVfY29tcCAlPiUgZGlzdGluY3QodGlzc3VlKSA9PSBocGFfY29tcCAlPiUgZGlzdGluY3QodGlzc3VlKSApCgpgYGAKI1JlYWQgb3J0aG9sb2cgZGF0YQpgYGB7cn0KI1NlbGVjdCBlaXRoZXIgIkhQQSIgb3IgImVuc2VtYmxlIgpvcnRob2xvZ19kYXRhX2Zyb20gPC0gIkhQQSIKI29ydGhvbG9nX2RhdGFfZnJvbSA8LSAiZW5zZW1ibGUiCgppbmNsdWRlX21hbnkybWFueSA8LSBUUlVFCmluY2x1ZGVfb25lMm1hbnkgPC0gVFJVRQoKI09ubHkgbmVlZGVkIGZvciBlbnNlbWJsOgojZGVmaW5lIGVuc2VtYmxlIHZlcnNpb24KdmVyc2lvbiAgICAgPC0gMTAzCm9yZ2FuaXNtX2RiIDwtICJybm9ydmVnaWN1c19nZW5lX2Vuc2VtYmwiCiNtYXJ0IDwtIHVzZUVuc2VtYmwgKCBiaW9tYXJ0PSJnZW5lcyIgLCBkYXRhc2V0PW9yZ2FuaXNtX2RiICwgdmVyc2lvbj12ZXJzaW9uICkKCmlmIChvcnRob2xvZ19kYXRhX2Zyb20gPT0gIkhQQSIpewogIGhvbW9sb2dfZGF0YSA8LSByZWFkLnRhYmxlKCIuL2RhdGEvaHVtYW5faHBhL2thbGxlL2Vuc2VtYmxfcmF0X29ydGhvbG9nLnRzdiIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFRSVUUpICU+JSAKICAgIGZpbHRlcihlbnNybm9nX2lkICVpbiUgcmF0X3Rpc3N1ZSR0YXJnZXRfaWQpICU+JSAKICAgIGZpbHRlcihlbnNnX2lkICVpbiUgaHVtYW5fYXRsYXNfdGlzc3VlJEdlbmUpCn0gZWxzZSBpZiAob3J0aG9sb2dfZGF0YV9mcm9tID09ICJlbnNlbWJsZSIpewogIGhvbW9sb2dfZGF0YSA8LSBnZXRCTSggYXR0cmlidXRlcz1jKCAiZW5zZW1ibF9nZW5lX2lkIiwgImhzYXBpZW5zX2hvbW9sb2dfZW5zZW1ibF9nZW5lIiwgImhzYXBpZW5zX2hvbW9sb2dfb3J0aG9sb2d5X3R5cGUiKSAsIG1hcnQ9bWFydCApICU+JQogICAgZmlsdGVyKGhzYXBpZW5zX2hvbW9sb2dfZW5zZW1ibF9nZW5lICE9ICIiKSAlPiUgCiAgICBmaWx0ZXIoZW5zZW1ibF9nZW5lX2lkICVpbiUgcmF0X3Rpc3N1ZSR0YXJnZXRfaWQpICU+JSAKICAgIGZpbHRlcihoc2FwaWVuc19ob21vbG9nX2Vuc2VtYmxfZ2VuZSAlaW4lIGh1bWFuX2F0bGFzX3Rpc3N1ZSRHZW5lKSAlPiUgCiAgICByZW5hbWUoZW5zcm5vZ19pZCA9IGVuc2VtYmxfZ2VuZV9pZCwgZW5zZ19pZCA9IGhzYXBpZW5zX2hvbW9sb2dfZW5zZW1ibF9nZW5lLCBvcnRob2xvZ190eXBlID0gaHNhcGllbnNfaG9tb2xvZ19vcnRob2xvZ3lfdHlwZSkKfQoKaWYgKGluY2x1ZGVfbWFueTJtYW55ID09IEZBTFNFKXsKICBob21vbG9nX2RhdGEgPC0gaG9tb2xvZ19kYXRhICU+JSBmaWx0ZXIoIW9ydGhvbG9nX3R5cGUgPT0gIm9ydGhvbG9nX21hbnkybWFueSIpCn0KaWYgKGluY2x1ZGVfb25lMm1hbnkgPT0gRkFMU0UpewogIGhvbW9sb2dfZGF0YSA8LSBob21vbG9nX2RhdGEgJT4lIGZpbHRlcighb3J0aG9sb2dfdHlwZSA9PSAib3J0aG9sb2dfb25lMm1hbnkiKQp9CgpgYGAKCgojRmlndXJlIDYKYGBge3J9CmNvbXBhcmlzb25fY29sb3JzX3RibCA8LSByYmluZCgKICBjb21wX21ldGFkYXRhICU+JSBzZWxlY3QobmFtZSA9IGNvbXBhcmlzb25fdGlzc3VlX25hbWUsIGNvbG9yID0gY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcikgJT4lIGRpc3RpbmN0KCksIAogIGNvbXBfbWV0YWRhdGEgJT4lIHNlbGVjdChuYW1lID0gdGlzc3VlX25hbWUsIGNvbG9yID0gdGlzc3VlX2NvbG9yKSAlPiUgIGRpc3RpbmN0KCkKICApICU+JSAKICBkaXN0aW5jdCgpICU+JSAKICBkcm9wX25hKCkgJT4lIAogIG11dGF0ZShuYW1lID0gc3RyX3RvX3NlbnRlbmNlKG5hbWUpKQoKcGFsIDwtIGNvbXBhcmlzb25fY29sb3JzX3RibCRjb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBzdHJfdG9fc2VudGVuY2UoY29tcGFyaXNvbl9jb2xvcnNfdGJsJG5hbWUpKQoKCnBsb3RfZGF0YTEgPC0gCiAgY29tcF9tZXRhZGF0YSAlPiUgCiAgc2VsZWN0KHRpc3N1ZV9uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBvcmdhbl9uYW1lKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKHRpc3N1ZV9uYW1lID0gc3RyX3RvX3NlbnRlbmNlKHRpc3N1ZV9uYW1lKSwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSA9IHN0cl90b19zZW50ZW5jZShjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSkgJT4lIAogIGFycmFuZ2Uob3JnYW5fbmFtZSwgY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShwbG90X29yZGVyID0gcm93X251bWJlcigpKSAlPiUgc2VsZWN0KC1vcmdhbl9uYW1lKQoKcGxvdF9kYXRhMiA8LSAKICBwbG90X2RhdGExICU+JQogIGdhdGhlcihjb2x1bW4sIGxhYmVsLCAtcGxvdF9vcmRlcikgJT4lCiAgZ3JvdXBfYnkobGFiZWwsIGNvbHVtbikgJT4lIAogIHN1bW1hcmlzZShwbG90X29yZGVyID0gbWVhbihwbG90X29yZGVyKSkgICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKGxhYmVsID0gbGFiZWwsCiAgICAgICAgIGNvbHVtbiA9IGZhY3Rvcihjb2x1bW4sCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ0aXNzdWVfbmFtZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY29tcGFyaXNvbl90aXNzdWVfbmFtZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpCnBsb3RfZGF0YTMgPC0gCiAgcGxvdF9kYXRhMSAlPiUgCiAgZ3JvdXBfYnkoY29tcGFyaXNvbl90aXNzdWVfbmFtZSkgJT4lIAogIG11dGF0ZShsZWZ0X3BvcyA9IG1lYW4ocGxvdF9vcmRlcikpCgpwbG90X2RhdGE0IDwtIAogIHBsb3RfZGF0YTIgJT4lIAogIGxlZnRfam9pbihjb21wYXJpc29uX2NvbG9yc190YmwsCiAgICAgICAgICAgIGJ5ID0gYygibGFiZWwiID0gIm5hbWUiKSkgJT4lIAogIGdyb3VwX2J5KGNvbHVtbiwgbGFiZWwpICU+JSAKICBzdW1tYXJpc2UobWlueSA9IG1pbihwbG90X29yZGVyKSAtIDAuNSwKICAgICAgICAgICAgbWF4eSA9IG1heChwbG90X29yZGVyKSArIDAuNSkKCmdncGxvdCgpICsKICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQsIAogICAgICAgICAgIGFlcyh4bWluID0gY29sdW1uLCB4bWF4ID0gY29sdW1uLCB5bWluID0gbWlueSwgeW1heCA9ICBtYXh5LCBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYKICAgICAgICAgKSArCiAgICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQgJT4lIGZpbHRlciAoY29sdW1uID09ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIiksIAogICAgICAgICAgIyBhZXMoeG1pbiA9IGNvbHVtbiwgeG1heCA9IGNvbHVtbiwgeW1pbiA9IG1pbnksIHltYXggPSBtYXh5LCBjb2xvciA9IGNvbG9yKSwgCiAgICAgICAgICAgYWVzKHhtaW4gPSAyLCB4bWF4ID0gMi41LCB5bWluID0gbWlueSwgeW1heCA9ICBtYXh5LCBmaWxsID0gbGFiZWwpLCAKICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYKICAgICAgICAgKSArCiAgICBnZW9tX3JlY3QoZGF0YSA9IHBsb3RfZGF0YTQgJT4lIGZpbHRlciAoY29sdW1uID09ICJ0aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMC41LCB4bWF4ID0gMSwgeW1pbiA9IG1pbnksIHltYXggPSAgbWF4eSwgZmlsbCA9IGxhYmVsKSwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCB3aWR0aCA9IDEwCiAgICAgICAgICkgKwogIGdlb21fc2VnbWVudCgKICAgIGRhdGEgPSBwbG90X2RhdGEzLAogICAgYWVzKAogICAgICB4ID0gImNvbXBhcmlzb25fdGlzc3VlX25hbWUiLAogICAgICB4ZW5kID0gInRpc3N1ZV9uYW1lIiwKICAgICAgeSA9IGxlZnRfcG9zLAogICAgICB5ZW5kID0gcGxvdF9vcmRlciwKICAgICAgY29sb3IgPSB0aXNzdWVfbmFtZQogICAgKSwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGFscGhhID0gMC41LAogICAgc2l6ZSA9IDIKICApKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBwbG90X2RhdGEyICU+JSBmaWx0ZXIoY29sdW1uID09ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIiksCiAgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgaGp1c3QgPSAwLAogICAgc2l6ZSA9IDIgKiA1IC8gNiwKICAgIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAsICJtbSIpCiAgKSArCiAgZ2VvbV90ZXh0KAogICAgZGF0YSA9IHBsb3RfZGF0YTIgJT4lIGZpbHRlcihjb2x1bW4gPT0gInRpc3N1ZV9uYW1lIiksCiAgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgaGp1c3QgPSAxLAogICAgc2l6ZSA9IDIgKiA1IC8gNiwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAsICJtbSIpCiAgKSArCiAgIyBnZW9tX2xhYmVsKAogICMgICBkYXRhID0gcGxvdF9kYXRhMiAlPiUKICAjICAgICBmaWx0ZXIoY29sdW1uID09ICJvcmdhbl9uYW1lIiksCiAgIyAgICNhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICMgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gZ3N1YigiICIsICJcbiIsIGxhYmVsKSksCiAgIyAgIHNob3cubGVnZW5kID0gRiwKICAjICAgbGFiZWwuc2l6ZSA9IDAsCiAgIyAgIGhqdXN0ID0gMSwKICAjICAgbGluZWhlaWdodCA9IDAuNywKICAjICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIiksCiAgIyAgIHNpemUgPSAyICogNSAvIDYKICAjICkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyAgPSBjKCJSYXQgdGlzc3VlIiwgIkNvbXBhcmlzb24gdGlzc3VlIikscG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KCkpCmdnc2F2ZSgiZmluYWxfcGxvdHMvY29tcGFyaXNvbi9jb21wYXJpc29uX3Rpc3N1ZXNfcmF0LnBkZiIsIGhlaWdodCA9IDUsIHdpZHRoID0gNCkKYGBgCmBgYHtyfQpjb21wYXJpc29uX2NvbG9yc190YmwgPC0gcmJpbmQoCiAgY29tcF9tZXRhZGF0YSAlPiUgc2VsZWN0KG5hbWUgPSBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBjb2xvciA9IGNvbnNlbnN1c190aXNzdWVfY29sb3IpICU+JSBkaXN0aW5jdCgpLCAKICBjb21wX21ldGFkYXRhICU+JSBzZWxlY3QobmFtZSA9IGhwYV9jb25zZW5zdXNfbmFtZSwgY29sb3IgPSB0aXNzdWVfY29sb3IpICU+JSAgZGlzdGluY3QoKQogICkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKG5hbWUgPSBzdHJfdG9fc2VudGVuY2UobmFtZSkpCgpwYWwgPC0gY29tcGFyaXNvbl9jb2xvcnNfdGJsJGNvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIHN0cl90b19zZW50ZW5jZShjb21wYXJpc29uX2NvbG9yc190YmwkbmFtZSkpCgoKcGxvdF9kYXRhMSA8LSAKICBjb21wX21ldGFkYXRhICU+JSAKICBzZWxlY3QoaHBhX2NvbnNlbnN1c19uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBvcmdhbl9uYW1lKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKGhwYV9jb25zZW5zdXNfbmFtZSA9IHN0cl90b19zZW50ZW5jZShocGFfY29uc2Vuc3VzX25hbWUpLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lID0gc3RyX3RvX3NlbnRlbmNlKGNvbXBhcmlzb25fdGlzc3VlX25hbWUpKSAlPiUgCiAgYXJyYW5nZShvcmdhbl9uYW1lLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSAlPiUgCiAgbXV0YXRlKHBsb3Rfb3JkZXIgPSByb3dfbnVtYmVyKCkpICU+JSBzZWxlY3QoLW9yZ2FuX25hbWUpCgpwbG90X2RhdGEyIDwtIAogIHBsb3RfZGF0YTEgJT4lCiAgZ2F0aGVyKGNvbHVtbiwgbGFiZWwsIC1wbG90X29yZGVyKSAlPiUKICBncm91cF9ieShsYWJlbCwgY29sdW1uKSAlPiUgCiAgc3VtbWFyaXNlKHBsb3Rfb3JkZXIgPSBtZWFuKHBsb3Rfb3JkZXIpKSAgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUobGFiZWwgPSBsYWJlbCwKICAgICAgICAgY29sdW1uID0gZmFjdG9yKGNvbHVtbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGMoImNvbXBhcmlzb25fdGlzc3VlX25hbWUiLCAiaHBhX2NvbnNlbnN1c19uYW1lIgogICAgICAgICAgICAgICAgICAgICAgICAgICApKSkKcGxvdF9kYXRhMyA8LSAKICBwbG90X2RhdGExICU+JSAKICBncm91cF9ieShjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSAlPiUgCiAgbXV0YXRlKGxlZnRfcG9zID0gbWVhbihwbG90X29yZGVyKSkKCnBsb3RfZGF0YTQgPC0gCiAgcGxvdF9kYXRhMiAlPiUgCiAgbGVmdF9qb2luKGNvbXBhcmlzb25fY29sb3JzX3RibCwKICAgICAgICAgICAgYnkgPSBjKCJsYWJlbCIgPSAibmFtZSIpKSAlPiUgCiAgZ3JvdXBfYnkoY29sdW1uLCBsYWJlbCkgJT4lIAogIHN1bW1hcmlzZShtaW55ID0gbWluKHBsb3Rfb3JkZXIpIC0gMC41LAogICAgICAgICAgICBtYXh5ID0gbWF4KHBsb3Rfb3JkZXIpICsgMC41KQoKZ2dwbG90KCkgKwogIGdlb21fcmVjdChkYXRhID0gcGxvdF9kYXRhNCwgCiAgICAgICAgICAgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gIG1heHksIGZpbGwgPSBsYWJlbCksIAogICAgICAgICAgIHNob3cubGVnZW5kID0gRgogICAgICAgICApICsKICAgIGdlb21fcmVjdChkYXRhID0gcGxvdF9kYXRhNCAlPiUgZmlsdGVyIChjb2x1bW4gPT0gImhwYV9jb25zZW5zdXNfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMiwgeG1heCA9IDIuNSwgeW1pbiA9IG1pbnksIHltYXggPSAgbWF4eSwgZmlsbCA9IGxhYmVsKSwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGCiAgICAgICAgICkgKwogICAgZ2VvbV9yZWN0KGRhdGEgPSBwbG90X2RhdGE0ICU+JSBmaWx0ZXIgKGNvbHVtbiA9PSAiY29tcGFyaXNvbl90aXNzdWVfbmFtZSIpLCAKICAgICAgICAgICMgYWVzKHhtaW4gPSBjb2x1bW4sIHhtYXggPSBjb2x1bW4sIHltaW4gPSBtaW55LCB5bWF4ID0gbWF4eSwgY29sb3IgPSBjb2xvciksIAogICAgICAgICAgIGFlcyh4bWluID0gMC41LCB4bWF4ID0gMSwgeW1pbiA9IG1pbnksIHltYXggPSAgbWF4eSwgZmlsbCA9IGxhYmVsKSwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCB3aWR0aCA9IDEwCiAgICAgICAgICkgKwogIGdlb21fc2VnbWVudCgKICAgIGRhdGEgPSBwbG90X2RhdGEzLAogICAgYWVzKAogICAgICB4ID0gImNvbXBhcmlzb25fdGlzc3VlX25hbWUiLAogICAgICB4ZW5kID0gImhwYV9jb25zZW5zdXNfbmFtZSIsCiAgICAgIHkgPSBsZWZ0X3BvcywKICAgICAgeWVuZCA9IHBsb3Rfb3JkZXIsCiAgICAgIGNvbG9yID0gaHBhX2NvbnNlbnN1c19uYW1lCiAgICApLAogICAgc2hvdy5sZWdlbmQgPSBGLAogICAgYWxwaGEgPSAwLjUsCiAgICBzaXplID0gMgogICkrCiAgZ2VvbV90ZXh0KAogICAgZGF0YSA9IHBsb3RfZGF0YTIgJT4lIGZpbHRlcihjb2x1bW4gPT0gImhwYV9jb25zZW5zdXNfbmFtZSIpLAogICAgYWVzKHggPSBjb2x1bW4sIHkgPSBwbG90X29yZGVyLCBsYWJlbCA9IGxhYmVsKSwKICAgIGhqdXN0ID0gMCwKICAgIHNpemUgPSAyICogNSAvIDYsCiAgICBsYWJlbC5wYWRkaW5nID0gdW5pdCgwLCAibW0iKQogICkgKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBwbG90X2RhdGEyICU+JSBmaWx0ZXIoY29sdW1uID09ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIiksCiAgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICAgaGp1c3QgPSAxLAogICAgc2l6ZSA9IDIgKiA1IC8gNiwKICAgIHNob3cubGVnZW5kID0gRiwKICAgIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAsICJtbSIpCiAgKSArCiAgIyBnZW9tX2xhYmVsKAogICMgICBkYXRhID0gcGxvdF9kYXRhMiAlPiUKICAjICAgICBmaWx0ZXIoY29sdW1uID09ICJvcmdhbl9uYW1lIiksCiAgIyAgICNhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gbGFiZWwpLAogICMgICBhZXMoeCA9IGNvbHVtbiwgeSA9IHBsb3Rfb3JkZXIsIGxhYmVsID0gZ3N1YigiICIsICJcbiIsIGxhYmVsKSksCiAgIyAgIHNob3cubGVnZW5kID0gRiwKICAjICAgbGFiZWwuc2l6ZSA9IDAsCiAgIyAgIGhqdXN0ID0gMSwKICAjICAgbGluZWhlaWdodCA9IDAuNywKICAjICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMCwgIm1tIiksCiAgIyAgIHNpemUgPSAyICogNSAvIDYKICAjICkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyAgPSBjKCJDb21wYXJpc29uIHRpc3N1ZSIsICJIdW1hbiBUaXNzdWUiKSxwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoKSkKZ2dzYXZlKCJmaW5hbF9wbG90cy9jb21wYXJpc29uL2NvbXBhcmlzb25fdGlzc3Vlc19odW1hbl9yZXYucGRmIiwgaGVpZ2h0ID0gNSwgd2lkdGggPSA0KQoKYGBgCgoKI0JhdGNoIENvcnJlY3Rpb24KYGBge3J9CgojRG8gYmF0Y2ggY29ycmVjdGlvbgojam9pbiByYXQgYW5kIGh1bWFuIHRtbSBkYXRhIHRvZ2V0aGVyCmpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXAgPC0gcmF0X3Rpc3N1ZV9jb21wICU+JQogIGlubmVyX2pvaW4oaG9tb2xvZ19kYXRhLCBieSA9IGMoInRhcmdldF9pZCIgPSAiZW5zcm5vZ19pZCIpKSAlPiUKICB1bml0ZShtdXR1YWxfaWQsIHRhcmdldF9pZCwgZW5zZ19pZCwgc2VwID0gIl8iKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9ICJyYXQiKSAlPiUKICBzZWxlY3QobXV0dWFsX2lkLCB0aXNzdWUsIHNwZWNpZXMgLCB0bW0pICU+JQogIGJpbmRfcm93cygKICAgIGhwYV9jb21wICU+JQogICAgICBpbm5lcl9qb2luKGhvbW9sb2dfZGF0YSwgYnkgPSBjKCJHZW5lIiA9ICJlbnNnX2lkIikpICU+JQogICAgICB1bml0ZShtdXR1YWxfaWQsIGVuc3Jub2dfaWQsIEdlbmUsIHNlcCA9ICJfIikgJT4lCiAgICAgIG11dGF0ZShzcGVjaWVzID0gImh1bWFuIikgJT4lCiAgICAgIHNlbGVjdChtdXR1YWxfaWQsIHRpc3N1ZSwgc3BlY2llcyAsIHRtbSkKICApCiAgIyMjIyBpZiB0bW0gaXMgdW5kZXIgMCB0aGVuIGl0IGlzIGVxdWFsIHRvIDAKICAjbXV0YXRlKHRtbSA9IGlmZWxzZSh0bW0gPCAxLCAwLCB0bW0gKSkKCiNsb25nIHRhYmxlIHdpdGggaW5mIG9mIG9uIHRpc3N1ZSBhbmQgc3BlY2llcwpqb2luZWRfYXRsYXNfY29tcGFyaXNvbl90ZW1wXzIgPC0gam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcCAlPiUgCiAgdW5pdGUoaWQsIHRpc3N1ZSwgc3BlY2llcywgc2VwID0gIl8iKSAlPiUgCiAgc2VwYXJhdGUoaWQsIGludG8gPSBjKCJ0aXNzdWUiLCAic3BlY2llcyIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEYpIAoKI3dpZGUgdGFibGUgb25seSB3aXRoIHRtbQpqb2luZWRfYXRsYXNfY29tcGFyaXNvbl90ZW1wM190bW0gPC0KICBqb2luZWRfYXRsYXNfY29tcGFyaXNvbl90ZW1wXzIgJT4lIAogIHNlbGVjdChtdXR1YWxfaWQsIGlkLCB0bW0pICU+JSAKICBzcHJlYWQoaWQsIHRtbSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygibXV0dWFsX2lkIikgCgojbG9nIHNjYWxlCmpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXAzX2xpbW1hIDwtCiAgam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcDNfdG1tICU+JQogIHtsb2cxMCguICsgMSl9ICU+JQogIGxpbW1hOjpyZW1vdmVCYXRjaEVmZmVjdChiYXRjaCA9IGNvbG5hbWVzKGpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXAzX3RtbSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2V4dHJhY3QoIl8uKiQiKSkKI3B1dCB0aGVtIHRvZ2V0aGVyIGluIHRoZSBsb25nIGZvcm1hdApqb2luZWRfYXRsYXNfY29tcGFyaXNvbjwtIAogIGpvaW5lZF9hdGxhc19jb21wYXJpc29uX3RlbXBfMiAlPiUKICBsZWZ0X2pvaW4oam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcDNfdG1tICU+JSAKICAgICAgICAgICAgICBhc190aWJibGUocm93bmFtZXMgPSAibXV0dWFsX2lkIikgJT4lIAogICAgICAgICAgICAgIGdhdGhlcihpZCwgdG1tLCAtMSkpICU+JSAKICBsZWZ0X2pvaW4oam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fdGVtcDNfbGltbWEgJT4lIAogICAgICAgICAgICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJtdXR1YWxfaWQiKSAlPiUgCiAgICAgICAgICAgICAgZ2F0aGVyKGlkLCBsaW1tYV9sb2cxcF90bW0sIC0xKSkgCmBgYAoKI0ZpZ3VyZSA3CiMjRmlndXJlIDdBIC0gQ3Jvc3Mgc3BlY2llcyBkZW5kcm9ncmFtbQpgYGB7cn0KCmhjbHVzdDRSTkFzZXEgPC0gZnVuY3Rpb24oZGYsIGNvcnJlbGF0aW9uX21ldGhvZCA9ICJzcGVhcm1hbiIpewogICN3aWRlIGRhdGFmcmFtZSBhcyBpbnB1dCAKICAjdG8gZ2V0IGNvcnJlbGF0aW9uIGJldHdlZW4gc2FtcGxlcywgd2hlcmUgcm93cyBhcmUgZ2VuZXMgY29sdW1ucyBhcmUgc2FtcGxlcwogICN0byBnZXQgY29ycmVsYXRpb24gYmV0d2VlbiBnZW5lcyBhY3Jvc3Mgc2FtcGxlcywgaW5wdXQgZGYgd2l0aCBnZW5lcyBhcyBjb2x1bW5zCiAgI2NhbiB1c2UgbGF0ZXIgZm9yIGRlbmRvZ3JhbSBtYWtpbmc6IGdnZGVuZHJvZ3JhbShbaGNsdXN0NFJOQXNlcV9yZXN1bHRzXSwgcm90YXRlID0gRkFMU0UsIHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIikKICBzaW1pbGFyaXR5IDwtIGNvcihkZiwgbWV0aG9kPWNvcnJlbGF0aW9uX21ldGhvZCwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQogIGRpc3NpbWlsYXJpdHkgPC0gMSAtIHNpbWlsYXJpdHkKICBoY2wgPC0gaGNsdXN0KGFzLmRpc3QoZGlzc2ltaWxhcml0eSksICJhdmVyYWdlIikKICByZXR1cm4gKGhjbCkKfSAKCgpvcmdhbl9jb2xvcnMgPC0gY29tcF9tZXRhZGF0YSAlPiUgc2VsZWN0KGNvbXBhcmlzb25fdGlzc3VlX25hbWUsIGNvbnNlbnN1c190aXNzdWVfY29sb3IpICU+JSBkcm9wX25hKCkgJT4lIGRpc3RpbmN0KCkKcGFsIDwtICBvcmdhbl9jb2xvcnMkY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBzdHJfdG9fc2VudGVuY2Uob3JnYW5fY29sb3JzJGNvbXBhcmlzb25fdGlzc3VlX25hbWUpKQoKc2hhcGVfZGVmIDwtIGMoMjEsIDIyKQpzaGFwZV9kZWYgPC0gc2V0TmFtZXMoc2hhcGVfZGVmLCBjKCJIdW1hbiIsICJSYXQiKSkKCgpwbG90X2NvbXBfZGVuZHJvX2RhdGEgPC0gam9pbmVkX2F0bGFzX2NvbXBhcmlzb24gJT4lIAogIHNlbGVjdChtdXR1YWxfaWQsIGlkLCBsaW1tYV9sb2cxcF90bW0pICU+JSAKICBzcHJlYWQoaWQsIGxpbW1hX2xvZzFwX3RtbSkgJT4lIAogIGNvbHVtbl90b19yb3duYW1lcygibXV0dWFsX2lkIikgJT4lIAogIGNvcihtZXRob2QgPSAic3BlYXJtYW4iLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogIHsxIC0gLn0gJT4lCiAgYXMuZGlzdCgpICU+JSAKICBoY2x1c3QobWV0aG9kID0gImF2ZXJhZ2UiKSAlVD4lCiAgcGxvdCAlPiUKICBkZW5kcm9fZGF0YSgpCgpkZW5kcm9fcGxvdF9kYXRhIDwtIAogIGxlZnRfam9pbihwbG90X2NvbXBfZGVuZHJvX2RhdGEkc2VnbWVudHMsIAogICAgICAgICAgICBwbG90X2NvbXBfZGVuZHJvX2RhdGEkbGFiZWxzLCAKICAgICAgICAgICAgYnkgPSBjKCJ4IiA9ICJ4IiwgInllbmQiID0gInkiKSkgJT4lIAogIHNlcGFyYXRlKGxhYmVsLCBjKCJ0aXNzdWUiLCAic3BlY2llcyIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgCiAgbXV0YXRlKHRpc3N1ZSA9IHN0cl90b19zZW50ZW5jZSh0aXNzdWUpLCBzcGVjaWVzID0gc3RyX3RvX3NlbnRlbmNlKHNwZWNpZXMpKSAlPiUgCiAgbXV0YXRlKHNwZWNpZXMgPSBmYWN0b3Ioc3BlY2llcywgYygiSHVtYW4iLCAiUmF0IikpKQoKbGVmdF9wbG90IDwtIAogIGRlbmRyb19wbG90X2RhdGEgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChhZXMoeD15LCB5PXgsIHhlbmQ9eWVuZCwgeWVuZD14ZW5kKSkrCiAgIyBnZW9tX3JlY3QoYWVzKHhtaW49MCwgeW1pbj14ICsgMC41LCAKICAjICAgICAgICAgICAgICAgeG1heD0tMC4wMiwgeW1heD14ZW5kIC0gMC41LCAKICAjICAgICAgICAgICAgICAgZmlsbCA9IHRpc3N1ZSksIAogICMgICAgICAgICAgIHNob3cubGVnZW5kID0gRikgKwogIGdlb21fcG9pbnQoYWVzKAogICAgICB4ID0gMCwKICAgICAgeSA9IHgsCiAgICAgIHNoYXBlID0gc3BlY2llcywKICAgICAgZmlsbCA9IHRpc3N1ZQogICAgKSwKICAgIGNvbG9yID0gImdyYXkyNSIsCiAgICBhbHBoYSA9IDEsCiAgICBzaXplID0gMywKICAgIHN0cm9rZSA9IDAuNwogICkgKwogIGdlb21fdGV4dChhZXMoeD0tMC4wMiwgeT14LAogICAgICAgICAgICAgICAgbGFiZWwgPSB0aXNzdWUpLAogICAgICAgICAgICBoanVzdCA9IDAsCiAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZV9kZWYgKSArCiMgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCwgZ3VpZGUgPSAibm9uZSIpICsKICAgc2NhbGVfeF9yZXZlcnNlKAogICAgIGV4cGFuZCA9IGV4cGFuc2lvbigwLjUgKSwgCiAgICAgcG9zaXRpb24gPSAidG9wIikrCiAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oMC4wMSkpICsKICB4bGFiKCIxIC0gU3BlYXJtYW4ncyByaG8iKSArCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwxLDEsMSksIHVuaXRzID0gIm1tIiksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpIApsZWZ0X3Bsb3QKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NvbXBhcmlzb24vY29tcGFyaXNvbl9kZW5kcm9ncmFtLnBkZiIsIHdpZHRoID0gNi41LCBoZWlnaHQgPSA5ICkKCmBgYAojI0ZpZ3VyZSA3QiAtIENyb3Mgc3BlY2llcyBVTUFQCmBgYHtyfQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShnZ3Bsb3RpZnkpCmxpYnJhcnkoZ2VvbXRleHRwYXRoKQoKY29tcF9tZXRhZGF0YSA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvY29tcGFyaXNvbl9tZXRhZGF0YS1pbml0LTEtMC5jc3YiKQp1bWFwX21ldGEgPC0gY29tcF9tZXRhZGF0YSAlPiUgc2VsZWN0KGNvbnNlbnN1c190aXNzdWVfY29sb3IsIG9yZ2FuX2NvbG9yLCBjb21wYXJpc29uX3Rpc3N1ZV9uYW1lKSAlPiUgZmlsdGVyKGNvbXBhcmlzb25fdGlzc3VlX25hbWUgIT0gIiIgKSAlPiUgZGlzdGluY3QoKQp3aWRlX2RhdGEgPC0gam9pbmVkX2F0bGFzX2NvbXBhcmlzb24gJT4lIAogIHNlbGVjdCgtdGlzc3VlLCAtc3BlY2llcywgLXRtbSkgJT4lIAogIHNwcmVhZChpZCwgbGltbWFfbG9nMXBfdG1tKQpzZWVkIDwtIDQyCmZpbHRlcl96ZXJvX3NkID0gRgpuX2Vwb2NocyA9IDEwMDAgCm5fbmVpZ2hib3JzID0gMTUKCnBjYV9yZXMgPC0KICB3aWRlX2RhdGEgJT4lCiAgI25vIGxvZzFwIGJlY2F1c2UgbGltbWEgdmFsdWUgaXMgYWxyZWFkeSBjYWxjdWxhdGVkIGZyb20gbG9nIHNjYWxlIHZhbHVlCiAgY29sdW1uX3RvX3Jvd25hbWVzKGNvbG5hbWVzKHdpZGVfZGF0YSlbMV0pICU+JQogIHNjYWxlKCkgJT4lCiAgdCgpICU+JQogIHBjYShuUGNzID0gZGltKC4pWzFdKQoKcGNfbGltIDwtCiAgd2hpY2gocGNhX3Jlc0BSMmN1bSA+IDAuOClbMV0KCnBjX2xpbV9zZCA8LQogIHJldih3aGljaChwY2FfcmVzQHNEZXYgPiAxKSlbMV0KCm5fbmVpZ2hib3JzID0gMTUKc2V0LnNlZWQoc2VlZCkKdW1hcF9yZXMgPC0gcGNhX3Jlc0BzY29yZXNbLCAxOnBjX2xpbV0gJT4lCiAgdXdvdDo6dW1hcChuX25laWdoYm9ycyA9IG5fbmVpZ2hib3JzLAogICAgICAgICAgICAgbl9lcG9jaHMgPSBuX2Vwb2NocykgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgc2V0X25hbWVzKHBhc3RlMCgiVU1BUCIsIDE6bmNvbCguKSkpICU+JQogIG11dGF0ZShzYW1wbGUgPSByb3duYW1lcyhwY2FfcmVzQHNjb3JlcykpICU+JQogIHNlbGVjdChzYW1wbGUsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgc2VwYXJhdGUoCiAgICBzYW1wbGUsCiAgICBpbnRvID0gYygidGlzc3VlIiwgInNwZWNpZXMiKSwKICAgIHNlcCA9ICJfIiwKICAgIHJlbW92ZSA9IEYKICApICU+JQogIGxlZnRfam9pbih1bWFwX21ldGEsIGJ5ID0gYygidGlzc3VlIiA9ICJjb21wYXJpc29uX3Rpc3N1ZV9uYW1lIikpCgpvcmdhbl9jb2xvcnMgPC0KICB1bWFwX21ldGEgJT4lIHNlbGVjdChjb21wYXJpc29uX3Rpc3N1ZV9uYW1lLCBjb25zZW5zdXNfdGlzc3VlX2NvbG9yKSAlPiUgdW5pcXVlKCkKcGFsIDwtICBvcmdhbl9jb2xvcnMkY29uc2Vuc3VzX3Rpc3N1ZV9jb2xvcgpwYWwgPC0gc2V0TmFtZXMocGFsLCBvcmdhbl9jb2xvcnMkY29tcGFyaXNvbl90aXNzdWVfbmFtZSkKCnNoYXBlX2RlZiA8LSBjKDIxLCAyMikKc2hhcGVfZGVmIDwtIHNldE5hbWVzKHNoYXBlX2RlZiwgYygiaHVtYW4iLCAicmF0IikpCgoKIyB1bWFwX3JlcyAlPiUgIGdncGxvdChhZXMoVU1BUDEsIFVNQVAyLCAgY29sb3IgPSBvcmdhbl9uYW1lKSkgKwojICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjgpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCkKcCA8LSB1bWFwX3JlcyAlPiUgIAogIGdncGxvdChhZXMoVU1BUDEsIFVNQVAyKSkgKwogIGdlb21fdGV4dHBhdGgoCiAgICBhZXMobGFiZWwgPSBzdHJfdG9fc2VudGVuY2UodGlzc3VlKSksCiAgICBoanVzdCA9IDAuNSwKICAgIHZqdXN0ID0gLTAuMywKICAgIGNvbG9yID0gImdyYXkyMCIKICApICsKICBnZW9tX3BvaW50KAogICAgYWVzKGZpbGwgPSB0aXNzdWUsIHNoYXBlID0gc3BlY2llcyksCiAgICBjb2xvciA9ICJncmF5MjUiLAogICAgYWxwaGEgPSAxLAogICAgc2l6ZSA9IDIsCiAgICBzdHJva2UgPSAwLjcKICApICsKICBndWlkZXMoZmlsbCA9ICJub25lIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZV9kZWYpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgICNsZWdlbmQudGl0bGUgPWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLTAuMSwgJ2NtJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoLTAuMDEsICdjbScpKSArCiAgZ3VpZGVzKHNoYXBlID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCBieXJvdyA9IFRSVUUsIHRpdGxlID0gIlNwZWNpZXMiKSkKCnAKCgoKCgppZiAoaW5jbHVkZV9tYW55Mm1hbnkgPT0gVFJVRSAmIGluY2x1ZGVfb25lMm1hbnkgPT0gVFJVRSkgewogIGNvbXBfdW1hcF9maWxlX25hbWUgPSBwYXN0ZTAoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3J0aG9sb2dfZGF0YV9mcm9tLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl91bWFwLnBkZiIpCn0gZWxzZSBpZiAoaW5jbHVkZV9tYW55Mm1hbnkgPT0gRkFMU0UgJiBpbmNsdWRlX29uZTJtYW55ID09IFRSVUUpIHsKICBjb21wX3VtYXBfZmlsZV9uYW1lID0gcGFzdGUwKCIuL2ZpbmFsX3Bsb3RzL2NvbXBhcmlzb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ydGhvbG9nX2RhdGFfZnJvbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfdW1hcC5wZGYiKQp9IGVsc2UgaWYgKGluY2x1ZGVfbWFueTJtYW55ID09IEZBTFNFICYKICAgICAgICAgICBpbmNsdWRlX29uZTJtYW55ID09IEZBTFNFKSB7CiAgY29tcF91bWFwX2ZpbGVfbmFtZSA9IHBhc3RlMCgiLi9maW5hbF9wbG90cy9jb21wYXJpc29uLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcnRob2xvZ19kYXRhX2Zyb20sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiX3VtYXAucGRmIikKfQoKCmdnc2F2ZShjb21wX3VtYXBfZmlsZV9uYW1lLCBoZWlnaHQgPSA1LjUsIHdpZHRoID0gNi41KQoKYGBgCgojI0ZpZ3VyZSA3QyAtIENyb3NzIHNwZWNpZXMgaHlwZXJnZW9tZXRyaWMgdGVzdApgYGB7cn0KI0Jhc2VkIG9uIE1heCBLYXJsc3NvbjogaHR0cHM6Ly9naXRodWIuY29tL21heGthcmxzc29uL1BpZy1BdGxhcy9ibG9iL21hc3Rlci9zY3JpcHRzL2Z1bmN0aW9uc19jbGFzc2lmaWNhdGlvbi5SCgpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dzY2kpCgoKCmNsYXNzMSA8LSBocGFfZ2VuZV9jbGFzc2lmaWNhdGlvbihkYXRhID0gcmF0X3Rpc3N1ZV9jb21wICU+JSBzZWxlY3QodGFyZ2V0X2lkLCB0aXNzdWUsIHRtbSksIGV4cHJlc3Npb25fY29sID0gInRtbSIsIHRpc3N1ZV9jb2wgPSAidGlzc3VlIiwgZ2VuZV9jb2wgPSAidGFyZ2V0X2lkIiwgZW5yX2ZvbGQgPSA0LCBtYXhfZ3JvdXBfbiA9IDUsIGRldF9saW0gPSAxKSAlPiUgcmVuYW1lKGVuc3Jub2dfaWQgPSBnZW5lKQpjbGFzczIgPC0gaHBhX2dlbmVfY2xhc3NpZmljYXRpb24oZGF0YSA9IGhwYV9jb21wICU+JSBzZWxlY3QoR2VuZSwgdGlzc3VlLCB0bW0pLCBleHByZXNzaW9uX2NvbCA9ICJ0bW0iLCB0aXNzdWVfY29sID0gInRpc3N1ZSIsIGdlbmVfY29sID0gIkdlbmUiLCBlbnJfZm9sZCA9IDQsIG1heF9ncm91cF9uID0gNSwgZGV0X2xpbSA9IDEpICU+JSAKICByZW5hbWUoZW5zZ19pZCA9IGdlbmUpCgpnZW5lX2NvbDEgPC0gImVuc3Jub2dfaWQiCmdlbmVfY29sMiA8LSAiZW5zZ19pZCIKc2VwIDwtICI7IgoKY2xhc3MxX2xvbmcgPC0KICBjbGFzczEgJT4lCiAgc2VsZWN0KGdlbmUxID0gZ2VuZV9jb2wxLCBlbnJpY2hlZF90aXNzdWVzKSAlPiUKICBtdXRhdGUoCiAgICBlbnJpY2hlZF90aXNzdWVzID0gaWZlbHNlKGlzLm5hKGVucmljaGVkX3Rpc3N1ZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibm90IGVucmljaGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5yaWNoZWRfdGlzc3VlcyksCiAgICBlbnJpY2hlZDEgPSBUCiAgKSAlPiUKICBzZXBhcmF0ZV9yb3dzKGVucmljaGVkX3Rpc3N1ZXMsIHNlcCA9IHNlcCkKCmNsYXNzMl9sb25nIDwtCiAgY2xhc3MyICU+JQogIHNlbGVjdChnZW5lMiA9IGdlbmVfY29sMiwgZW5yaWNoZWRfdGlzc3VlcykgJT4lCiAgbXV0YXRlKAogICAgZW5yaWNoZWRfdGlzc3VlcyA9IGlmZWxzZShpcy5uYShlbnJpY2hlZF90aXNzdWVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5vdCBlbnJpY2hlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaGVkX3Rpc3N1ZXMpLAogICAgZW5yaWNoZWQyID0gVAogICkgJT4lCiAgc2VwYXJhdGVfcm93cyhlbnJpY2hlZF90aXNzdWVzLCBzZXAgPSBzZXApCgp0aXNfIDwtCiAgdW5pcXVlKGMoY2xhc3MxX2xvbmckZW5yaWNoZWRfdGlzc3VlcywKICAgICAgICAgICBjbGFzczJfbG9uZyRlbnJpY2hlZF90aXNzdWVzKSkgJT4lCiAgc29ydCgpCgpnZW5lX29ydGhvbG9ncyA8LSBob21vbG9nX2RhdGEgJT4lIHNlbGVjdCgxLDIpCgpvdmVybGFwX2h5cGVyX2FsbCA8LSBleHBhbmQuZ3JpZChpZDEgPSB0aXNfLAogICAgICAgICAgICBpZDIgPSB0aXNfLAogICAgICAgICAgICBnZW5lID0gMTpucm93KGdlbmVfb3J0aG9sb2dzKSkgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgbGVmdF9qb2luKGdlbmVfb3J0aG9sb2dzICU+JQogICAgICAgICAgICAgIHNlbGVjdChnZW5lMSA9IGdlbmVfY29sMSwKICAgICAgICAgICAgICAgICAgICAgZ2VuZTIgPSBnZW5lX2NvbDIpICU+JQogICAgICAgICAgICAgIG11dGF0ZShnZW5lID0gcm93X251bWJlcigpKSkgJT4lCiAgc2VsZWN0KC1nZW5lKSAlPiUKICBsZWZ0X2pvaW4oY2xhc3MxX2xvbmcsCiAgICAgICAgICAgIGJ5ID0gYygiZ2VuZTEiLCAiaWQxIiA9ICJlbnJpY2hlZF90aXNzdWVzIikpICU+JQogIGxlZnRfam9pbihjbGFzczJfbG9uZywKICAgICAgICAgICAgYnkgPSBjKCJnZW5lMiIsICJpZDIiID0gImVucmljaGVkX3Rpc3N1ZXMiKSkgJT4lCiAgbXV0YXRlKAogICAgZW5yaWNoZWQxID0gaWZlbHNlKGlzLm5hKGVucmljaGVkMSksCiAgICAgICAgICAgICAgICAgICAgICAgRiwKICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2hlZDEpLAogICAgZW5yaWNoZWQyID0gaWZlbHNlKGlzLm5hKGVucmljaGVkMiksCiAgICAgICAgICAgICAgICAgICAgICAgRiwKICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2hlZDIpCiAgKSAlPiUKICBncm91cF9ieShpZDEsIGlkMiwgZW5yaWNoZWQxLCBlbnJpY2hlZDIpICU+JQogIGNvdW50KCkgJT4lCiAgZ3JvdXBfYnkoaWQxLCBpZDIpICU+JQogIHN1bW1hcmlzZSgKICAgICMgcSBpcyB0aGUgbnVtYmVyIG9mIHN1Y2Nlc3NlcwogICAgcSA9IHN1bShuW3doaWNoKGVucmljaGVkMSAmIGVucmljaGVkMildKSwKICAgIAogICAgIyBrIGlzIHRoZSBudW1iZXIgb2YgdHJpZXMgLSBpLmUuIHRoZSBudW1iZXIgb2YgZ2VuZXMgdGhhdCBhcmUgZWxldmF0ZWQgZm9yIGVpdGhlciBzcGVjaWVzCiAgICBrID0gc3VtKG5bd2hpY2goZW5yaWNoZWQxIHwgZW5yaWNoZWQyKV0pLAogICAgCiAgICAjIG0gaXMgdGhlIG51bWJlciBvZiBwb3NzaWJsZSBzdWNjZXNzZXMgLSBpLmUuIHRoZSBudW1iZXIgb2YgZ2VuZXMgdGhhdCBhcmUgZWxldmF0ZWQgZm9yIGVpdGhlcgogICAgbSA9IG1pbihzdW0oblt3aGljaChlbnJpY2hlZDEpXSksCiAgICAgICAgICAgIHN1bShuW3doaWNoKGVucmljaGVkMildKSksCiAgICAKICAgICMgbiBpcyB0aGUgcG9wdWxhdGlvbiBzaXplIC0gaS5lLiB0aGUgbnVtYmVyIG9mIGdlbmVzCiAgICBuID0gc3VtKG4pIC0gbQogICkgJT4lIAogIG11dGF0ZShwX3ZhbHVlID0gcGh5cGVyKHEgLSAxLCBtLCBuLCBrLCBsb3dlci50YWlsID0gRikpICU+JQogIG11dGF0ZShwX3ZhbHVlID0gaWZlbHNlKHBfdmFsdWUgPT0gMCwgLk1hY2hpbmUkZG91YmxlLnhtaW4sIHBfdmFsdWUpLAogICAgICAgICBhZGpfcHZhbCA9IHAuYWRqdXN0KHBfdmFsdWUsIG1ldGhvZCA9ICJCSCIpKSAlPiUKICByZW5hbWUocmF0X2lkID0gaWQxLCAKICAgICAgICAgaHVtYW5faWQgPSBpZDIpCgpvdmVybGFwX2h5cGVyX2FsbCAlPiUgCiAgd3JpdGVfY3N2KCIuL2RhdGEvZmluYWxfZGF0YS9yYXRfaHVtYW5fY2xhc3NfaHlwZXIuY3N2IikKCgpwbG90X29yZGVyIDwtIAogIG92ZXJsYXBfaHlwZXJfYWxsICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHJhdF9pZCA9IHN0cl90b19zZW50ZW5jZShyYXRfaWQpLAogICAgICAgICBodW1hbl9pZCA9IHN0cl90b19zZW50ZW5jZShodW1hbl9pZCkpICU+JQogIGZpbHRlcihyYXRfaWQgPT0gaHVtYW5faWQpICU+JQogIGFycmFuZ2UoYWRqX3B2YWwpICU+JQogIHB1bGwocmF0X2lkKQoKCnN0cmlwcGVkX3RoZW1lIDwtCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG91ciA9IE5BKSwKICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9IE5BKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gTkEpLAogICAgICAgICNsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAjbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQua2V5LnNpemU9IHVuaXQoMC4zLCAiY20iKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIiksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91cj0iYmxhY2siLHNpemU9MC41KSkKCgpvdmVybGFwX2h5cGVyX2FsbCAlPiUgCiAgZ3JvdXBfYnkocmF0X2lkLCBodW1hbl9pZCkgJT4lCiAgbXV0YXRlKGNhcHBlZF9wID0gbWluKGMoLWxvZzEwKGFkal9wdmFsKSwgMjApKSkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUocmF0X2lkID0gc3RyX3RvX3NlbnRlbmNlKHJhdF9pZCksCiAgICAgICAgIGh1bWFuX2lkID0gc3RyX3RvX3NlbnRlbmNlKGh1bWFuX2lkKSkgJT4lCiAgbXV0YXRlKHJhdF9pZCA9IGZhY3RvcihyYXRfaWQsIHBsb3Rfb3JkZXIpLAogICAgICAgICBodW1hbl9pZCA9IGZhY3RvcihodW1hbl9pZCwgcGxvdF9vcmRlcikpICU+JQogIGdncGxvdChhZXMoaHVtYW5faWQsIHJhdF9pZCwgZmlsbCA9IGNhcHBlZF9wKSkgKwogIGdlb21fdGlsZSgpICsgCiAgZ2VvbV90aWxlKGRhdGEgPSAuICU+JSAKICAgICAgICAgICAgICBmaWx0ZXIoYWRqX3B2YWwgPj0gMC4wNSksCiAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAiRCIsIGRpcmVjdGlvbiA9IDEsIAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkFkanVzdGVkIHAtdmFsdWUiKSArIAogIHN0cmlwcGVkX3RoZW1lICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IC05MCwgaGp1c3QgPSAwLCB2anVzdCA9IDAuMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICB4bGFiKCJIdW1hbiIpICsgCiAgeWxhYigiUmF0IikKCiNnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi9PdmVybGFwIGh5cGVyIGhlYXRtYXAgZWxldmF0ZWQucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA2KSAgCgpvdmVybGFwX2h5cGVyX2FsbCAlPiUgCiAgZmlsdGVyKGh1bWFuX2lkICE9ICJub3QgZW5yaWNoZWQiICYgCiAgICAgICAgICAgcmF0X2lkICE9ICJub3QgZW5yaWNoZWQiKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHJhdF9pZCA9IHN0cl90b19zZW50ZW5jZShyYXRfaWQpLAogICAgICAgICBodW1hbl9pZCA9IHN0cl90b19zZW50ZW5jZShodW1hbl9pZCkpICU+JQogIGZpbHRlcihhZGpfcHZhbCA8IDAuMDUpICU+JQogIG11dGF0ZShyYXRfaWQgPSBmYWN0b3IocmF0X2lkLCBwbG90X29yZGVyKSwKICAgICAgICAgaHVtYW5faWQgPSBmYWN0b3IoaHVtYW5faWQsIHBsb3Rfb3JkZXIpLAogICAgICAgICBhZGpfcHZhbCA9IGNhc2Vfd2hlbihhZGpfcHZhbCA8IDFlLTEwMCB+IDFlLTEwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVCB+IGFkal9wdmFsKSkgJT4lCiAgZ2dwbG90KGFlcyhodW1hbl9pZCwgcmF0X2lkLCBmaWxsID0gLWxvZzEwKGFkal9wdmFsKSwgc2l6ZSA9IC1sb2cxMChhZGpfcHZhbCkpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCAKICAgICAgICAgICAgIGFscGhhID0gMC44LAogICAgICAgICAgICAgc3Ryb2tlID0gMC4yLAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArIAogICMgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAiRSIsIGRpcmVjdGlvbiA9IDEsIAogICMgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQWRqdXN0ZWQgcC12YWx1ZSIpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gZ2dzY2k6OnJnYl9tYXRlcmlhbChwYWxldHRlID0gImRlZXAtb3JhbmdlIikpICsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDYpKSArCiAgc3RyaXBwZWRfdGhlbWUgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gLTkwLCBoanVzdCA9IDAsIHZqdXN0ID0gMC4yKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5OTAiLCBzaXplID0gMC4yKSkgKwogIHhsYWIoIkh1bWFuIikgKyAKICB5bGFiKCJSYXQiKQoKZ2dzYXZlKCIuL2ZpbmFsX3Bsb3RzL2NvbXBhcmlzb24vaHlwZXJnZW9tZXRyaWNfY29tcGFyaXNvbl90aXNzdWUucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1LjUpICAKCmBgYAoKIyNGaWd1cmUgN0QgLSBDcm9zcyBzcGVjaWVzIGdlbmUgYW5ub3RhdGlvbiBhbGx1dmlhbApgYGB7cn0KCiNjbGFzc2lmaWNhdGlvbiByYXQKY2xhc3NpZmljYXRpb25fcmF0X2NvbXAgPC0KICBocGFfZ2VuZV9jbGFzc2lmaWNhdGlvbigKICAgIGRhdGEgPSBqb2luZWRfYXRsYXNfY29tcGFyaXNvbiAlPiUgZmlsdGVyKHNwZWNpZXMgPT0gInJhdCIpICU+JSBzZWxlY3QoYygtaWQsLXNwZWNpZXMsLWxpbW1hX2xvZzFwX3RtbSkpLAogICAgZXhwcmVzc2lvbl9jb2wgPSAidG1tIiwKICAgIHRpc3N1ZV9jb2wgPSAidGlzc3VlIiwKICAgIGdlbmVfY29sID0gIm11dHVhbF9pZCIsCiAgICBlbnJfZm9sZCA9IDQsCiAgICBtYXhfZ3JvdXBfbiA9IDUsCiAgICBkZXRfbGltID0gMQogICkKCnJhdF9jb21wX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKAogIGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAicmF0IikgJT4lIHNlbGVjdCgtaWQsLXNwZWNpZXMsLWxpbW1hX2xvZzFwX3RtbSkgICU+JQogICAgc3ByZWFkKHRpc3N1ZSwgdG1tKSAlPiUKICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7CiAgICAgIGxvZzEwKHggKyAxKQogICAgfSkgJT4lCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoIm11dHVhbF9pZCIpCikKCmNsYXNzaWZpY2F0aW9uX3JhdF9jb21wIDwtIGNsYXNzaWZpY2F0aW9uX3JhdF9jb21wICU+JQogIGxlZnRfam9pbihyYXRfY29tcF90YXUsIGJ5ID0gYygiZ2VuZSIgPSAiZ2VuZSIpKSAlPiUKICBtdXRhdGUodGF1X3Njb3JlID0gaWZlbHNlKHNwZWNfY2F0ZWdvcnkgPT0gIm5vdCBkZXRlY3RlZCIsIE5BLCB0YXVfc2NvcmUpKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9ICJSYXQiKQoKCiNjbGFzc2lmaWNhdGlvbiBodW1hbgoKY2xhc3NpZmljYXRpb25faHVtYW5fY29tcCA8LQogIGhwYV9nZW5lX2NsYXNzaWZpY2F0aW9uKAogICAgZGF0YSA9IGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiaHVtYW4iKSAlPiUgc2VsZWN0KGMoLWlkLC1zcGVjaWVzLC1saW1tYV9sb2cxcF90bW0pKSwKICAgIGV4cHJlc3Npb25fY29sID0gInRtbSIsCiAgICB0aXNzdWVfY29sID0gInRpc3N1ZSIsCiAgICBnZW5lX2NvbCA9ICJtdXR1YWxfaWQiLAogICAgZW5yX2ZvbGQgPSA0LAogICAgbWF4X2dyb3VwX24gPSA1LAogICAgZGV0X2xpbSA9IDEKICApCgpodW1hbl9jb21wX3RhdSA8LSBjYWxjdWxhdGVfdGF1X3Njb3JlKAogIGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiaHVtYW4iKSAlPiUgc2VsZWN0KC1pZCwtc3BlY2llcywtbGltbWFfbG9nMXBfdG1tKSAgJT4lCiAgICBzcHJlYWQodGlzc3VlLCB0bW0pICU+JQogICAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHsKICAgICAgbG9nMTAoeCArIDEpCiAgICB9KSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygibXV0dWFsX2lkIikKKQoKY2xhc3NpZmljYXRpb25faHVtYW5fY29tcCA8LSBjbGFzc2lmaWNhdGlvbl9odW1hbl9jb21wICU+JQogIGxlZnRfam9pbihyYXRfY29tcF90YXUsIGJ5ID0gYygiZ2VuZSIgPSAiZ2VuZSIpKSAlPiUKICBtdXRhdGUodGF1X3Njb3JlID0gaWZlbHNlKHNwZWNfY2F0ZWdvcnkgPT0gIm5vdCBkZXRlY3RlZCIsIE5BLCB0YXVfc2NvcmUpKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9ICJIdW1hbiIpCgpgYGAKCgpgYGB7cn0KYWxsdXZpYWxfZGF0YV9jb21wIDwtIGNsYXNzaWZpY2F0aW9uX3JhdF9jb21wICU+JQogIHNlbGVjdChnZW5lLCBzcGVjX2NhdGVnb3J5KSAlPiUKICByZW5hbWUoUmF0ID0gc3BlY19jYXRlZ29yeSkgJT4lCiAgbGVmdF9qb2luKAogICAgY2xhc3NpZmljYXRpb25faHVtYW5fY29tcCAlPiUKICAgICAgc2VsZWN0KGdlbmUsIHNwZWNfY2F0ZWdvcnkpICU+JQogICAgICByZW5hbWUoSHVtYW4gPSBzcGVjX2NhdGVnb3J5KSwgCiAgICBieSA9ICJnZW5lIgogICkKCgp3aWR0aCA9IDAuMQoKYWxsdXZfMSA8LQogIAogIGFsbHV2aWFsX2RhdGFfY29tcCAlPiUKICBtdXRhdGUoUmF0ID0gc3RyX3RvX3NlbnRlbmNlKFJhdCksCiAgICAgICAgICBIdW1hbiA9IHN0cl90b19zZW50ZW5jZShIdW1hbikpICU+JQogIHNlbGVjdChSYXQsIAogICAgICAgICBIdW1hbikgJT4lCiAgbXV0YXRlKHJvd19uID0gcm93X251bWJlcigpKSAlPiUKICBnYXRoZXIoYmFyLCBjaHVuaywgLXJvd19uKSAlPiUKICBtdXRhdGUoY29sb3JfdmFycyA9IDEpICU+JQogIGdyb3VwX2J5KHJvd19uKSAlPiUKICBtdXRhdGUoY2h1bmtfY29sb3IgPSBjaHVua1ttYXRjaChjKCJIdW1hbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmF0IilbY29sb3JfdmFyc10sIGJhcildKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIAogIAogIG11dGF0ZShjaHVuayA9IGZhY3RvcihjaHVuaywgbGV2ZWxzID0gYygnVGlzc3VlIGVucmljaGVkJywgJ0dyb3VwIGVucmljaGVkJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdUaXNzdWUgZW5oYW5jZWQnLCAnTG93IHRpc3N1ZSBzcGVjaWZpY2l0eScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTm90IGRldGVjdGVkJykpLAogICAgICAgICBiYXIgPSBmYWN0b3IoYmFyLCBsZXZlbHMgPSBjKCJIdW1hbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJhdCIpKSkgJT4lCiAgCiAgCiAgZ2dwbG90KGFlcyh4ID0gYmFyLCBzdHJhdHVtID0gY2h1bmssIGFsbHV2aXVtID0gcm93X24sCiAgICAgICAgICAgICB5ID0gMSkpICsKICAKICBnZW9tX2Zsb3coYWVzKGZpbGwgPSBjaHVua19jb2xvciksIAogICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYsIHdpZHRoID0gd2lkdGgsCiAgICAgICAgICAgIGtub3QucG9zID0gMS82KSArCiAgZ2VvbV9zdHJhdHVtKGFlcyhmaWxsID0gY2h1bmspLCAKICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLCBjb2xvciA9IE5BLCB3aWR0aCA9IHdpZHRoKSArCiAgCiAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBjKC4xLCAuMSksIHBvc2l0aW9uID0gInRvcCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKGdlbmVfY2F0ZWdvcnlfcGFsKSkgKyAKICAKICAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgY29vcmRfZmxpcCgpCgojIGFsbHV2XzEKCmZsb3dfZGF0YSA8LQogIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1sxXV0gJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgewogICAgaWYgKCJzaWRlIiAlaW4lIG5hbWVzKC4pKSB7CiAgICAgIC4KICAgIH0gZWxzZXsKICAgICAgbXV0YXRlKC4sCiAgICAgICAgICAgICBzaWRlID0gY2FzZV93aGVuKGZsb3cgPT0gImZyb20iIH4gInN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxvdyA9PSAidG8iIH4gImVuZCIpKQogICAgfQogIH0KCgoKc3RyYXR1bV9kYXRhIDwtIAogIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1syXV0KCmZsb3dfZGF0YV9sYWJlbHMgPC0KICBmbG93X2RhdGEgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgc2VsZWN0KHgsIHN0cmF0dW0sIGdyb3VwLCBzaWRlLCB5bWluLCB5bWF4KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc2lkZSwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGMoeCwgc3RyYXR1bSwgeW1pbiwgeW1heCkpICU+JQogIG11dGF0ZV9hdCgKICAgIGMoCiAgICAgICJ4X2VuZCIsCiAgICAgICJ5bWF4X2VuZCIsCiAgICAgICJ5bWluX2VuZCIsCiAgICAgICJ4X3N0YXJ0IiwKICAgICAgInltYXhfc3RhcnQiLAogICAgICAieW1pbl9zdGFydCIKICAgICksCiAgICBhcy5udW1lcmljCiAgKSAlPiUKICBncm91cF9ieShzdHJhdHVtX3N0YXJ0LCBzdHJhdHVtX2VuZCwgeF9zdGFydCwgeF9lbmQpICU+JQogIHN1bW1hcmlzZSgKICAgIHlfZW5kID0gKG1pbih5bWluX2VuZCkgKyBtYXgoeW1heF9lbmQpKSAvIDIsCiAgICB5X3N0YXJ0ID0gKG1pbih5bWluX3N0YXJ0KSArIG1heCh5bWF4X3N0YXJ0KSkgLyAyLAogICAgc2l6ZSA9IG1heCh5bWF4X3N0YXJ0KSAtIG1pbih5bWluX3N0YXJ0KQogICkKCmFsbHV2XzIgPC0gCiAgYWxsdXZfMSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBmbG93X2RhdGFfbGFiZWxzLAogICAgICAgICAgICBhZXMoeCA9IHhfc3RhcnQgKyB3aWR0aC8yLAogICAgICAgICAgICAgICAgeSA9IHlfc3RhcnQsIAogICAgICAgICAgICAgICAgbGFiZWwgPSBzaXplKSwgCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRiwgCiAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICBhbmdsZSA9IC05MCwKICAgICAgICAgICAgaGp1c3QgPSAxLAogICAgICAgICAgICB2anVzdCA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gZmxvd19kYXRhX2xhYmVscywKICAgICAgICAgICAgYWVzKHggPSB4X2VuZCAtIHdpZHRoLzIsCiAgICAgICAgICAgICAgICB5ID0geV9lbmQsIAogICAgICAgICAgICAgICAgbGFiZWwgPSBzaXplKSwgCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRiwgCiAgICAgICAgICAgIHNpemUgPSAzLCAKICAgICAgICAgICAgYW5nbGUgPSAtOTAsCiAgICAgICAgICAgIGhqdXN0ID0gMCwKICAgICAgICAgICAgdmp1c3QgPSAwLjUpICsKICAKICAjIFN0cmF0dW0gbGFiZWwKICAKICBnZW9tX3RleHQoZGF0YSA9IHN0cmF0dW1fZGF0YSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeCA9PSAxKSwKICAgICAgICAgICAgYWVzKHggPSB4IC0gd2lkdGgvMiwKICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgbGFiZWwgPSBzdHJhdHVtKSwKICAgICAgICAgICAgc2l6ZSA9IDQsIAogICAgICAgICAgICB2anVzdCA9IDEuNSwKICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGKSArIAogIGdlb21fdGV4dChkYXRhID0gc3RyYXR1bV9kYXRhICU+JQogICAgICAgICAgICAgIGZpbHRlcih4ID09IDIpLAogICAgICAgICAgICBhZXMoeCA9IHggKyB3aWR0aC8yLAogICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICBsYWJlbCA9IHN0cmF0dW0pLAogICAgICAgICAgICBzaXplID0gNCwgCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwKICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGKSArIAogIAogIGdlb21fdGV4dChkYXRhID0gc3RyYXR1bV9kYXRhLAogICAgICAgICAgICBhZXMoeCA9IHgsIAogICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICBsYWJlbCA9IHltYXggLSB5bWluKSwgCiAgICAgICAgICAgIHNpemUgPSA0LCAKICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwKICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGKQoKCmFsbHV2XzIKCmlmIChpbmNsdWRlX21hbnkybWFueSA9PSBUUlVFICYgaW5jbHVkZV9vbmUybWFueSA9PSBUUlVFKXsKICBjb21wX2FsbHV2X2ZpbGVfbmFtZSA9IHBhc3RlMCgiLi9maW5hbF9wbG90cy9jb21wYXJpc29uLyIsb3J0aG9sb2dfZGF0YV9mcm9tLCJfY29tcGFyaXNvbl9hbGxfYWxsdXZpYWwucGRmIikKfSBlbHNlIGlmIChpbmNsdWRlX21hbnkybWFueSA9PSBGQUxTRSAmIGluY2x1ZGVfb25lMm1hbnkgPT0gVFJVRSl7CiAgY29tcF9hbGx1dl9maWxlX25hbWUgPSBwYXN0ZTAoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi8iLG9ydGhvbG9nX2RhdGFfZnJvbSwiX2NvbXBhcmlzb25fb25seV9vbjJvbmUybWFueV9hbGx1dmlhbC5wZGYiKQp9IGVsc2UgaWYgKGluY2x1ZGVfbWFueTJtYW55ID09IEZBTFNFICYgaW5jbHVkZV9vbmUybWFueSA9PSBGQUxTRSl7CiAgY29tcF9hbGx1dl9maWxlX25hbWUgPSBwYXN0ZTAoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi8iLG9ydGhvbG9nX2RhdGFfZnJvbSwiX2NvbXBhcmlzb25fb25seV9vbmUyb25lX2FsbHV2aWFsLnBkZiIpfQoKZ2dzYXZlKGNvbXBfYWxsdXZfZmlsZV9uYW1lLHdpZHRoID0gOCwgaGVpZ2h0ID0gMykKCmBgYAoKI0ZpZ3VyZSBTMSAtIEJyYWluIE1ldGFkYXRhIGFsbHV2aWFsIHBsb3QKYGBge3J9CiNGdW5jdGlvbiBhZGFwdGVkIGZyb20gTWF4IEthcmxzc29uCm11bHRpX2FsbHV2aWFsX3Bsb3QgPC0KICBmdW5jdGlvbihkYXRhLAogICAgICAgICAgIHZhcnMsCiAgICAgICAgICAgY2h1bmtfbGV2ZWxzLAogICAgICAgICAgIHBhbCwKICAgICAgICAgICBjb2xvcl9ieSA9IGMoMSwgMywgMykpIHsKICAgIHNlbHZhcnMgPSB2YXJzCiAgICAKICAgIGlmICghaXMubnVsbChuYW1lcyh2YXJzKSkpIHsKICAgICAgdmFycyA9IG5hbWVzKHZhcnMpCiAgICB9CiAgICAKICAgIGFsbHV2XzEgPC0KICAgICAgZGF0YSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3Qoc2VsdmFycykgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgbXV0YXRlKHJvd19uID0gcm93X251bWJlcigpKSAlPiUKICAgICAgZ2F0aGVyKGJhciwgY2h1bmssLXJvd19uKSAlPiUKICAgICAgbGVmdF9qb2luKHRpYmJsZShiYXIgPSB2YXJzLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX3ZhcnMgPSBjb2xvcl9ieSksCiAgICAgICAgICAgICAgICBieSA9ICJiYXIiKSAlPiUKICAgICAgZ3JvdXBfYnkocm93X24pICU+JQogICAgICBtdXRhdGUoY2h1bmtfY29sb3IgPSBjaHVua1ttYXRjaCh2YXJzW2NvbG9yX3ZhcnNdLCBiYXIpXSkgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgCiAgICAgIG11dGF0ZShjaHVuayA9IGZhY3RvcihjaHVuaywgbGV2ZWxzID0gY2h1bmtfbGV2ZWxzKSwKICAgICAgICAgICAgIGJhciA9IGZhY3RvcihiYXIsIGxldmVscyA9IHZhcnMpKSAlPiUKICAgICAgCiAgICAgIAogICAgICBnZ3Bsb3QoYWVzKAogICAgICAgIHggPSBiYXIsCiAgICAgICAgc3RyYXR1bSA9IGNodW5rLAogICAgICAgIGFsbHV2aXVtID0gcm93X24sCiAgICAgICAgeSA9IDEKICAgICAgKSkgKwogICAgICAKICAgICAgZ2VvbV9mbG93KGFlcyhmaWxsID0gY2h1bmtfY29sb3IpLAogICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArCiAgICAgIGdlb21fc3RyYXR1bShhZXMoZmlsbCA9IGNodW5rKSwKICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRiwgY29sb3IgPSBOQSkgKwogICAgICAKICAgICAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBjKC4xLCAuMSksIHBvc2l0aW9uID0gInRvcCIpICsKICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwoKCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgKQoKCiAgICAKICAgIAogICAgZmxvd19kYXRhIDwtCiAgICAgIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1sxXV0gJT4lCiAgICAgIGFzX3RpYmJsZSgpICU+JQogICAgICB7CiAgICAgICAgaWYgKCJzaWRlIiAlaW4lIG5hbWVzKC4pKSB7CiAgICAgICAgICAuCiAgICAgICAgfSBlbHNlewogICAgICAgICAgbXV0YXRlKC4sCiAgICAgICAgICAgICAgICAgc2lkZSA9IGNhc2Vfd2hlbihmbG93ID09ICJmcm9tIiB+ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmbG93ID09ICJ0byIgfiAiZW5kIikpCiAgICAgICAgfQogICAgICB9CiAgICAKICAgIAogICAgc3RyYXR1bV9kYXRhIDwtCiAgICAgIGdncGxvdF9idWlsZChhbGx1dl8xKSRkYXRhW1syXV0KICAgIAogICAgZmxvd19kYXRhX2xhYmVscyA8LQogICAgICBmbG93X2RhdGEgJT4lCiAgICAgIGFzX3RpYmJsZSgpICU+JQogICAgICAKICAgICAgc2VsZWN0KHgsIHN0cmF0dW0sIGdyb3VwLCBzaWRlLCB5bWluLCB5bWF4KSAlPiUKICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNpZGUsCiAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYyh4LCBzdHJhdHVtLCB5bWluLCB5bWF4KSkgJT4lCiAgICAgIAogICAgICBtdXRhdGVfYXQoCiAgICAgICAgYygKICAgICAgICAgICJ4X2VuZCIsCiAgICAgICAgICAieW1heF9lbmQiLAogICAgICAgICAgInltaW5fZW5kIiwKICAgICAgICAgICJ4X3N0YXJ0IiwKICAgICAgICAgICJ5bWF4X3N0YXJ0IiwKICAgICAgICAgICJ5bWluX3N0YXJ0IgogICAgICAgICksCiAgICAgICAgYXMubnVtZXJpYwogICAgICApICU+JQogICAgICBncm91cF9ieShzdHJhdHVtX3N0YXJ0LCBzdHJhdHVtX2VuZCwgeF9zdGFydCwgeF9lbmQpICU+JQogICAgICBzdW1tYXJpc2UoCiAgICAgICAgeV9lbmQgPSAobWluKHltaW5fZW5kKSArIG1heCh5bWF4X2VuZCkpIC8gMiwKICAgICAgICB5X3N0YXJ0ID0gKG1pbih5bWluX3N0YXJ0KSArIG1heCh5bWF4X3N0YXJ0KSkgLyAyLAogICAgICAgIHNpemUgPSBtYXgoeW1heF9zdGFydCkgLSBtaW4oeW1pbl9zdGFydCkKICAgICAgKQogICAgCiAgICBhbGx1dl8xIDwtCiAgICAgIGFsbHV2XzEgKwogICAgICAjIGdlb21fdGV4dCgKICAgICAgIyAgIGRhdGEgPSBmbG93X2RhdGFfbGFiZWxzLAogICAgICAjICAgYWVzKHggPSB4X3N0YXJ0ICsgMSAvIDYsCiAgICAgICMgICAgICAgeSA9IHlfc3RhcnQsCiAgICAgICMgICAgICAgbGFiZWwgPSBzaXplKSwKICAgICAgIyAgIGluaGVyaXQuYWVzID0gRiwKICAgICAgIyAgIHNpemUgPSAzLAogICAgICAjICAgaGp1c3QgPSAwCiAgICAgICMgKSArCiAgICAgICMgZ2VvbV90ZXh0KAogICAgICAjICAgZGF0YSA9IGZsb3dfZGF0YV9sYWJlbHMsCiAgICAgICMgICBhZXMoeCA9IHhfZW5kIC0gMSAvIDYsCiAgICAgICMgICAgICAgeSA9IHlfZW5kLAogICAgICAjICAgICAgIGxhYmVsID0gc2l6ZSksCiAgICAgICMgICBpbmhlcml0LmFlcyA9IEYsCiAgICAgICMgICBzaXplID0gMywKICAgICAgIyAgIGhqdXN0ID0gMQogICAgICAjICkgKwogICAgICAjIAogICAgICAjIFN0cmF0dW0gbGFiZWwKICAgICAgZ2VvbV90ZXh0KAogICAgICAgIGRhdGEgPSBzdHJhdHVtX2RhdGEsCiAgICAgICAgYWVzKAogICAgICAgICAgeCA9IHgsCiAgICAgICAgICB5ID0geSwKICAgICAgICAgIGxhYmVsID0gcGFzdGUoc3RyYXR1bSMsIAogICAgICAgICAgICAgICAgICAgICAgICMgcGFzdGUoIlsiLCB5bWF4IC0geW1pbiwgIl0iLCBzZXAgPSAiIikKICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICksCiAgICAgICAgc2l6ZSA9IDQsCiAgICAgICAgaW5oZXJpdC5hZXMgPSBGCiAgICAgICkKICAgICAgICAgICAgICAgIAogICAgCiAgICBhbGx1dl8xCiAgfQpgYGAKCmBgYHtyfQptZXRhZGF0YV9iIDwtIG1ldGFkYXRhICU+JSBmaWx0ZXIob3JnYW5fbmFtZSA9PSJCcmFpbiIpCgp0X25hbWVzIDwtIG1ldGFkYXRhX2IkdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCnJfbmFtZXMgPC0gbWV0YWRhdGFfYiRyZWdpb25fdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCmNfbmFtZXMgPC0gbWV0YWRhdGFfYiRjb25zZW5zdXNfdGlzc3VlX25hbWUgJT4lIHVuaXF1ZSgpCm9fbmFtZXMgPC0gbWV0YWRhdGFfYiRvcmdhbl9uYW1lICU+JSB1bmlxdWUoKQoKdF9jb2xvcnMgPC0gbWV0YWRhdGFfYiAlPiUgc2VsZWN0KHRpc3N1ZV9uYW1lLCB0aXNzdWVfY29sb3IpICU+JSB1bmlxdWUoKSAlPiUgcmVuYW1lIChjaHVuayA9IHRpc3N1ZV9uYW1lLCBjb2xvciA9IHRpc3N1ZV9jb2xvcikKcl9jb2xvcnMgPC0gbWV0YWRhdGFfYiAlPiUgc2VsZWN0KHJlZ2lvbl90aXNzdWVfbmFtZSwgcmVnaW9uX3Rpc3N1ZV9jb2xvcikgJT4lIHVuaXF1ZSgpICU+JSByZW5hbWUgKGNodW5rID0gcmVnaW9uX3Rpc3N1ZV9uYW1lLCBjb2xvciA9IHJlZ2lvbl90aXNzdWVfY29sb3IpCmNfY29sb3JzIDwtIG1ldGFkYXRhX2IgJT4lIHNlbGVjdChjb25zZW5zdXNfdGlzc3VlX25hbWUsIGNvbnNlbnN1c190aXNzdWVfY29sb3IpICU+JSB1bmlxdWUoKSAlPiUgcmVuYW1lIChjaHVuayA9IGNvbnNlbnN1c190aXNzdWVfbmFtZSwgY29sb3IgPSBjb25zZW5zdXNfdGlzc3VlX2NvbG9yKQpvX2NvbG9ycyA8LSBtZXRhZGF0YV9iICU+JSBzZWxlY3Qob3JnYW5fbmFtZSwgb3JnYW5fY29sb3IpICU+JSB1bmlxdWUoKSAlPiUgcmVuYW1lIChjaHVuayA9IG9yZ2FuX25hbWUsIGNvbG9yID0gb3JnYW5fY29sb3IpCgpiaW5kX2NvbG9ycyA8LSBiaW5kX3Jvd3ModF9jb2xvcnMsIHJfY29sb3JzLCBvX2NvbG9ycykgJT4lIHVuaXF1ZSgpICU+JSBhcnJhbmdlKGNodW5rKSAlPiUgbXV0YXRlKHJvd19uID0gcm93X251bWJlcigpKQpwYWwgPC0gIGJpbmRfY29sb3JzJGNvbG9yCnBhbCA8LSBzZXROYW1lcyhwYWwsIGJpbmRfY29sb3JzJGNodW5rKQoKZGF0YSA9IG1ldGFkYXRhX2IKdmFycyA9IGMoInRpc3N1ZV9uYW1lIiwgInJlZ2lvbl90aXNzdWVfbmFtZSIsICJvcmdhbl9uYW1lIikKY2h1bmtfbGV2ZWxzID0gYyh0X25hbWVzLCByX25hbWVzLCBvX25hbWVzKSAlPiUgdW5pcXVlKCkKY29sb3JfYnkgPSBjKDEsIDMsIDMpCgptdWx0aV9hbGx1dmlhbF9wbG90KGRhdGEgPSBtZXRhZGF0YV9iLCB2YXJzID0gdmFycywgY2h1bmtfbGV2ZWxzID0gY2h1bmtfbGV2ZWxzLCBwYWwgPSBwYWwsIGNvbG9yX2J5ID0gYygxLCAzLCAzKSkKCmdnc2F2ZSgiZmluYWxfcGxvdHMvYWxsdXZpYWwvYnJhaW4tdGNvLTFfcC5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDEwKQpgYGAKCgojRmlndXJlIFMyIC0gTm9ybWFsaXNhdGlvbiBjb21wYXJpc29uIFRQTSB2cyBuVFBNIChUTU0pCmBgYHtyfQp0cG1fc2FtcGxlIDwtcmVhZF9jc3YoIi4vZGF0YS9maW5hbF9kYXRhL2N1cmF0ZWRfcFRQTV9yYXR0dXNfbm9ydmVnaWN1c192MTAzLmNzdiIpCgoKc2FtcGxlX3N1YnNldF9JRCA8LSBtZXRhZGF0YVttYXRjaCh1bmlxdWUobWV0YWRhdGEkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSwgbWV0YWRhdGEkY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSxdICU+JSBzZWxlY3QoSUQpCnNhbXBsZV9zdWJzZXRfdG1tIDwtIHRtbV9zYW1wbGUgJT4lIHNlbGVjdCh0YXJnZXRfaWQsIHNhbXBsZV9zdWJzZXRfSUQkSUQpICU+JSBnYXRoZXIoc2FtcGxlLCB0bW0sIC0xKQpzYW1wbGVfc3Vic2V0X3RwbSA8LSB0cG1fc2FtcGxlICU+JSBzZWxlY3QodGFyZ2V0X2lkLCBzYW1wbGVfc3Vic2V0X0lEJElEKSAlPiUgZ2F0aGVyKHNhbXBsZSwgdHBtLCAtMSkKCiMgcGxvdF9kYXRhIDwtIGxlZnRfam9pbihzYW1wbGVfc3Vic2V0X3RtbSwgc2FtcGxlX3N1YnNldF90cG0sIGJ5ID0gYygidGFyZ2V0X2lkIiwgInNhbXBsZSIpKSAlPiUKIyAgIG11dGF0ZShsb2cxcF90bW0gPSBsb2cxMCh0bW0gKyAxKSwgbG9nMXBfdHBtID0gbG9nMTAodHBtICsxKSkgJT4lIHNlbGVjdCgtdG1tLCAtdHBtKSAlPiUgCiMgICBnYXRoZXIoZXhwcmVzc2lvbl90eXBlLCBleHByZXNzaW9uLCAtMSwgLTIpICU+JSAKIyAgIGxlZnRfam9pbihtZXRhZGF0YSAlPiUgc2VsZWN0KElELCB0aXNzdWVfbmFtZSwgY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSwgYnkgPSBjKCJzYW1wbGUiID0gIklEIikpCgpwbG90X2RhdGEgPC0gbGVmdF9qb2luKHNhbXBsZV9zdWJzZXRfdG1tLCBzYW1wbGVfc3Vic2V0X3RwbSwgYnkgPSBjKCJ0YXJnZXRfaWQiLCAic2FtcGxlIikpICU+JQogIGdhdGhlcihleHByZXNzaW9uX3R5cGUsIGV4cHJlc3Npb24sIC0xLCAtMikgJT4lIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICBsb2cxMCh4ICsgMSkKICAgICAgICAgICAgICB9KSAlPiUgCiAgbGVmdF9qb2luKG1ldGFkYXRhICU+JSBzZWxlY3QoSUQsIHRpc3N1ZV9uYW1lLCBjb25zZW5zdXNfdGlzc3VlX25hbWUpLCBieSA9IGMoInNhbXBsZSIgPSAiSUQiKSkKCgpwYWxfdGJsIDwtIG1ldGFkYXRhICU+JSBzZWxlY3QoY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lLCBjb25zZW5zdXNfdGlzc3VlX2NvbG9yKSAlPiUgZGlzdGluY3QoKQpwYWwgPC0gcGFsX3RibCAlPiUgcHVsbChjb25zZW5zdXNfdGlzc3VlX2NvbG9yKQpwYWwgPC0gc2V0TmFtZXMocGFsLCBwYWxfdGJsICU+JSBwdWxsKGNvbnNlbnN1c190aXNzdWVfbmFtZSkpCgoKZ2dwbG90KGRhdGEgPSBwbG90X2RhdGEsIGFlcyh4ID0gZXhwcmVzc2lvbiwgeSA9c2FtcGxlLCBmaWxsID0gY29uc2Vuc3VzX3Rpc3N1ZV9uYW1lKSkgKwogIGdlb21fYm94cGxvdChkcmF3X3F1YW50aWxlcyA9IDAuNSwgb3V0bGllci5zaXplID0gMC41LCBvdXRsaWVyLmFscGhhID0gMC4zKSsKICBmYWNldF93cmFwKH5leHByZXNzaW9uX3R5cGUpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy90cG1fdG1tX2NvbXBfYm94cGxvdC5wZGYiLCB3aWR0aD03LCBoZWlnaHQgPSAxMikKCmBgYAoKI0ZpZ3VyZSBTMyAtIFNwZWFybWFuIGhlYXRtYXAgKHRpc3N5ZSB0eXBlIGxldmVsKQpgYGB7cn0KCiMjU3BlYXJtYW4ncyByb2ggaGVhdG1hcCBhdCB0aXNzdWUgdHlwZSBsZXZlbAppZihmaWxlLmV4aXN0cygiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpKSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpCn0gZWxzZSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSAgdG1tX3Rpc3N1ZSAlPiUKICAgIHNwcmVhZCh0aXNzdWVfbmFtZSwgdG1tKSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIikKICB3cml0ZV9jc3YoYXMuZGF0YS5mcmFtZSh0aXNzdWVfdG1tX3NwZWFybWFuKSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIiksIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfdGlzc3Vlcy5jc3YiKQp9Cgp0aXNzdWVfdG1tX3NwZWFybWFuICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoInRpc3N1ZV9uYW1lIikgJT4lIAogIHBoZWF0bWFwKAogICAjIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLAogICAgY2VsbGhlaWdodCA9IDgsCiAgICBjZWxsd2lkdGggPSA4LCAKICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgY29sb3IgPSB2aXJpZGlzOjppbmZlcm5vKDIwLCBkaXJlY3Rpb24gPSAtMSksCiAgICBzaG93X3Jvd25hbWVzID0gRkFMU0UsIAogICAgKSAlPiUgCiAgYXMuZ2dwbG90KCkKCmdnc2F2ZSgiLi9maW5hbF9wbG90cy9kYXRhX3ByZXNlbnRhdGlvbi9zcGVhcm1hbl9jb3JyX3Rpc3N1ZS5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCkKCgpgYGAKCiNGaWd1cmUgUzQgLSBDb21wYXJpc29uIFBoZWF0bWFwCmBgYHtyfQppZihmaWxlLmV4aXN0cygiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpKSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSByZWFkX2NzdigiLi9kYXRhL2ZpbmFsX2RhdGEvc3BlYXJtYW5fY29ycl90aXNzdWVzLmNzdiIpCn0gZWxzZSB7CiAgdGlzc3VlX3RtbV9zcGVhcm1hbiA8LSAgdG1tX3Rpc3N1ZSAlPiUKICAgIHNwcmVhZCh0aXNzdWVfbmFtZSwgdG1tKSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygidGFyZ2V0X2lkIikgJT4lCiAgICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIikKICB3cml0ZV9jc3YoYXMuZGF0YS5mcmFtZSh0aXNzdWVfdG1tX3NwZWFybWFuKSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gInRpc3N1ZV9uYW1lIiksIi4vZGF0YS9maW5hbF9kYXRhL3NwZWFybWFuX2NvcnJfdGlzc3Vlcy5jc3YiKQp9CgoKCmpvaW5lZF9hdGxhc19jb21wYXJpc29uX3BoZWF0X2RhdGEgPC0gIGpvaW5lZF9hdGxhc19jb21wYXJpc29uICU+JQogIHNlbGVjdChtdXR1YWxfaWQsIGlkLGxpbW1hX2xvZzFwX3RtbSApICU+JSAgCiAgc3ByZWFkKGlkLCBsaW1tYV9sb2cxcF90bW0pICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoIm11dHVhbF9pZCIpICU+JSAKICBjb3IobWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAidGlzc3VlX25hbWUiKQoKam9pbmVkX2F0bGFzX2NvbXBhcmlzb25fcGhlYXRfZGF0YSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJ0aXNzdWVfbmFtZSIpICU+JSAKICBwaGVhdG1hcCgKICAgIyBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwKICAgIGNlbGxoZWlnaHQgPSA4LAogICAgY2VsbHdpZHRoID0gOCwgCiAgICBib3JkZXJfY29sb3IgPSBOQSwKICAgIGNvbG9yID0gdmlyaWRpczo6aW5mZXJubygyMCwgZGlyZWN0aW9uID0gLTEpLAogICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCAKICAgICkgJT4lIAogIGFzLmdncGxvdCgpCgpnZ3NhdmUoIi4vZmluYWxfcGxvdHMvY29tcGFyaXNvbi9waGVhdG1hcC5wZGYiLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAyMCkKCmBgYApgYGB7cn0Kc2Vzc2lvbkluZm8oKQoKYGBgCgo=